Swift — Как объединить два неупорядоченных массива структур в массив новых объектов на основе совпадающих значений?

#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. Это здорово. Мне все еще нужно это протестировать, но выглядит намного лучше, чем то, что я делал. И да, ха-ха, это был просто пример. Я не слишком люблю выкладывать свой фактический код. Большое спасибо 🙂