#arrays #swift #class #object
#массивы #swift #класс #объект
Вопрос:
У меня есть два разных массива пользовательских структур (скажем, [NameAndAge] и [FullName] ). Я хотел бы объединить их в массив новых объектов (назовем его FullNamesAndAges ). Я думаю, что нужно перебирать их, сопоставляя имена, чтобы создать новый объект.
Если мои структуры выглядят так:
struct NameAndAge {
let name: String
let age: Int
}
struct FullName {
let firstName: String
let lastName: String
}
И мой новый объект выглядит так:
class PersonViewModel {
let firstName: String
let lastName: String
var age: Int
// with initializer
}
Как мне это сделать? Прямо сейчас я использую две карты, но мне интересно, есть ли более короткий / чистый / эффективный / лучший (вставьте предпочитаемое прилагательное) способ сделать это? Я полностью понимаю, что это может быть субъективным, поскольку это вопрос мнения. Я просто хочу знать, есть ли более быстрый способ добиться этого.
Что у меня сейчас есть:
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)
}
}
}
Для меня это выглядит очень неаккуратно, поэтому я надеюсь, что есть «лучший» способ сделать это. Я не могу использовать zip
, поскольку они неупорядочены.
Комментарии:
1. Могут ли быть дубликаты имен в любом из массивов? Что происходит тогда? Сколько элементов должно быть в каждом массиве? Это определит, для чего вы, возможно, захотите оптимизировать
Ответ №1:
Ваш код компактен, но он не очень эффективен. Если бы в ваших массивах были тысячи записей, вы бы проводили миллионы сравнений.
Вместо этого сначала создайте a Dictionary
, который позволяет вам искать NameAndAge
запись на основе имени. Словари очень эффективны для поиска.
Затем используйте этот словарь в compactMap
fullNames
массиве для создания конечного массива 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)
}
Примечание: я предполагаю, что это всего лишь пример, потому что поиск возраста человека на основе его имени на самом деле не очень хорошая идея из-за коллизий имен.
Комментарии:
1. Это здорово. Мне все еще нужно это протестировать, но выглядит намного лучше, чем то, что я делал. И да, ха-ха, это был просто пример. Я не слишком люблю выкладывать свой фактический код. Большое спасибо 🙂