0

I have two different arrays of custom Structs, (let's say [NameAndAge] and [FullName]). I would like to combine them into an array of new objects (let's call it FullNamesAndAges). My thinking is to iterate over them, matching first names in order to create the new object.

If my structs look like this:

struct NameAndAge {
    let name: String
    let age: Int
}

struct FullName {
    let firstName: String
    let lastName: String
}

And my new object looks like this:

class PersonViewModel {
    let firstName: String
    let lastName: String
    var age: Int
    // with initializer
}

How would I accomplish this? Right now, I am using two maps, but I am wondering if there is a shorter/cleaner/more-efficient/better (insert your preferred adjective) way to do this? I completely understand that this may be subjective, since it's a matter of opinion. I just want to know if there is a faster way to accomplish this.

What I currently have:

let namesAndAges: [NameAndAge] = // pretend this is an array of 10+ structs, unordered
let fullNames: [FullName] = // pretend this is an array of 10+ structs, unordered


let people = namesAndAges.compactMap { nameAge in
                        fullNames.compactMap { fullName in
                              if fullName.firstName == nameAge.name {
                                       return PersonViewModel(fullName.firstName, fullName.lastName, nameAge.age)
                              }
                        }
              }

To me that looks super sloppy, which is why I'm hoping there's a 'better' way to do it. I can't use zip since they're unordered.

1
  • Could there be duplicates of names in either of the arrays? What happens then? How many elements are expected to be in each array? This would determine what you might want to optimize for Commented Oct 10, 2020 at 2:46

1 Answer 1

1

Your code is compact, but it isn't very efficient. If your arrays had thousands of entries, you'd be doing millions of comparisons.

Instead, first create a Dictionary that allows you to lookup a NameAndAge record based upon the name. Dictionaries are very efficient for lookup.

Then use that dictionary in the compactMap of the fullNames array to create the final array of PersonViewModels:

let namesAndAges: [NameAndAge] = [] // this is an array of 10+ structs, unordered
let fullNames: [FullName] = [] // this is an array of 10+ structs, unordered

// Create a dictionary to efficiently map name to `NameAndAge` struct
var nameAndAgeLookup = [String : NameAndAge]()
namesAndAges.forEach { nameAndAgeLookup[$0.name] = $0 }

let people = fullNames.compactMap { fullName -> PersonViewModel? in
    guard let age = nameAndAgeLookup[fullName.firstName]?.age else { return nil }
    return PersonViewModel(fullName.firstName, fullName.lastName, age)
}

Note: I assume this is just an example, because looking up someone's age based on their firstName is not really a good idea due to name collisions.

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

1 Comment

That's great. I still have to test it but looks way better than what I was doing. And yes, haha, that was just an example. I'm not too fond of putting up my actual code up. Thanks a bunch :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.