Reputation: 231
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.
Upvotes: 0
Views: 271
Reputation: 154573
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 PersonViewModel
s:
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.
Upvotes: 1