1

So, I have the following objects:

struct Person {
   let name: String
   let birthday: String
}

let Bob = Person(name: "Bob", birthday: "11.12.1987")
let Tim = Person(name: "Tim", birthday: "11.12.1987")
let John = Person(name: "John", birthday: "01.02.1957")
let Jerry = Person(name: "Jerry", birthday: "17.12.2001")

And the following array:

let people = [Bob, Tim, John, Jerry]

My goal is to generate a dictionary from this array, with "birthday" for the key and the "Person" object itself as the value: [String: [Person]]. If there are equal keys the person should be added and form an array as a key. So the result will be the following:

dictionary = ["11.12.1987": [Bob, Tim], "01.02.1957": John, "17.12.2001": Jerry]

What is the best way to achieve this?

Cheers!

2 Answers 2

3

You could use Dictionary(grouping:by:) method together with mapValues,

let result = Dictionary(grouping: people, by: \.birthday)

print(result)

Output is,

["01.02.1957": ["John"], "17.12.2001": ["Jerry"], "11.12.1987": ["Bob", "Tim"]]

This would yield you a Dictionary of type Dictionary<String, [Person]>.

From, your question it seems like you want a Person if there is only one Person and array when there are multiple people. You would loose type in that situation and your type would be something like this, Dictionary<String, Any>. You could make small bit of modification to the above code to do that.

let result = Dictionary(grouping: people, by: \.birthday)
    .mapValues { people -> Any in
                if people.count == 1 {
                    return people.first!
                }
                return people
            }

print(result)

The output would be similar to what you want,

["01.02.1957": "John", "17.12.2001": "Jerry", "11.12.1987": ["Bob", "Tim"]]

Sign up to request clarification or add additional context in comments.

3 Comments

OP said the "Person" object itself as the value: [String: [Person]] So, not need for so much trouble. Just use Dictionary(grouping:by:) initializer.
For some reason, I seem to miss, the part of your code: "Dictionary(grouping: people, by: { $0.birthday })" works fine, without the .mapValue part. Thanks a bunch
Oh I got it, you parsing only the names for a key, rather the the object "Person"
0

What about a loop through the dictionary checking if date exists. Otherwise create key with date and assign value. If key already exists append person to the actual key value:

var dictionary = [String: [Person]]()
        
        // Loop through all person objects in array
        for person in people {
            
            // Check if date already exists
            if dictionary[person.birthday] != nil {
                // If already exists, include new person in array
                var array = dictionary[person.birthday]
                array!.append(person)
            }
            // If date does not exists, add new key with date and person
            else {
                dictionary[person.birthday] = [person]
            }
            
        }

3 Comments

Thank you, that works fine. But I was looking for more "elegant" may of doing it, something like Sandeep suggested
let dict = Dictionary(grouping: people, by: \.birthday)
This is fine, but it's reinventing a wheel. Everytime you copy/paste a pattern like this you introduce an opportunity for mistakes and bugs. You should leverage the standard library where possible.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.