У меня есть массив Person
объектов:
class Person {
let name:String
let position:Int
}
и массив:
let myArray = [p1,p1,p3]
Я хочу отобразить myArray
словарь [position:name]
классического решения:
var myDictionary = [Int:String]()
for person in myArray {
myDictionary[person.position] = person.name
}
есть ли элегантный способ Свифт , чтобы сделать это с функциональным подходом map
, flatMap
... или другой современный стиль Swift
ios
arrays
swift
dictionary
iOSGeek
источник
источник
[position:name]
или[position:[name]]
? Если у вас есть два человека в одной позиции, ваш словарь сохранит только последний из встреченных в вашем цикле ... У меня есть аналогичный вопрос, для которого я пытаюсь найти решение, но я хочу, чтобы результат был похож на[1: [p1, p3], 2: [p2]]
Dictionary(uniqueKeysWithValues: array.map{ ($0.key, $0) })
Ответы:
Хорошо,
map
это не очень хороший пример, потому что это то же самое, что и цикл, вы можете использоватьreduce
вместо этого, потребовалось объединить каждый ваш объект и превратить его в одно значение:let myDictionary = myArray.reduce([Int: String]()) { (dict, person) -> [Int: String] in var dict = dict dict[person.position] = person.name return dict } //[2: "b", 3: "c", 1: "a"]
В Swift 4 или выше используйте ответ ниже для более четкого синтаксиса.
источник
Поскольку
Swift 4
вы можете использовать подход @Tj3n более чисто и эффективно, используяinto
версию,reduce
он избавляется от временного словаря и возвращаемого значения, поэтому его легче и быстрее читать.Пример настройки кода:
struct Person { let name: String let position: Int } let myArray = [Person(name:"h", position: 0), Person(name:"b", position:4), Person(name:"c", position:2)]
Into
параметру передается пустой словарь типа результата:let myDict = myArray.reduce(into: [Int: String]()) { $0[$1.position] = $1.name }
Непосредственно возвращает словарь переданного типа
into
:print(myDict) // [2: "c", 0: "h", 4: "b"]
источник
$0
представляет: этоinout
словарь, который мы «возвращаем», хотя на самом деле не возвращаемся, поскольку его изменение эквивалентно возврату из-за егоinout
-ности.Начиная со Swift 4, это можно сделать очень легко. Есть два новых инициализатора, которые создают словарь из последовательности кортежей (пары ключ и значение). Если ключи гарантированно уникальны, вы можете сделать следующее:
let persons = [Person(name: "Franz", position: 1), Person(name: "Heinz", position: 2), Person(name: "Hans", position: 3)] Dictionary(uniqueKeysWithValues: persons.map { ($0.position, $0.name) })
=>
[1: "Franz", 2: "Heinz", 3: "Hans"]
Это приведет к ошибке выполнения, если какой-либо ключ дублируется. В этом случае вы можете использовать эту версию:
let persons = [Person(name: "Franz", position: 1), Person(name: "Heinz", position: 2), Person(name: "Hans", position: 1)] Dictionary(persons.map { ($0.position, $0.name) }) { _, last in last }
=>
[1: "Hans", 2: "Heinz"]
Это ведет себя как ваш цикл for. Если вы не хотите «перезаписывать» значения и придерживаться первого сопоставления, вы можете использовать это:
Dictionary(persons.map { ($0.position, $0.name) }) { first, _ in first }
=>
[1: "Franz", 2: "Heinz"]
Swift 4.2 добавляет третий инициализатор, который группирует элементы последовательности в словарь. Ключи словаря выводятся замыканием. Элементы с одинаковым ключом помещаются в массив в том же порядке, что и в последовательности. Это позволяет достичь результатов, аналогичных приведенным выше. Например:
Dictionary(grouping: persons, by: { $0.position }).mapValues { $0.last! }
=>
[1: Person(name: "Hans", position: 1), 2: Person(name: "Heinz", position: 2)]
Dictionary(grouping: persons, by: { $0.position }).mapValues { $0.first! }
=>
[1: Person(name: "Franz", position: 1), 2: Person(name: "Heinz", position: 2)]
источник
[1: [Person(name: "Franz", position: 1)], 2: [Person(name: "Heinz", position: 2)], 3: [Person(name: "Hans", position: 3)]]
. Не ожидаемый результат, но при желании он может быть преобразован в плоский словарь.Вы можете написать собственный инициализатор для
Dictionary
типа, например, из кортежей:extension Dictionary { public init(keyValuePairs: [(Key, Value)]) { self.init() for pair in keyValuePairs { self[pair.0] = pair.1 } } }
а затем используйте
map
для своего массиваPerson
:var myDictionary = Dictionary(keyValuePairs: myArray.map{($0.position, $0.name)})
источник
Как насчет решения на основе KeyPath?
extension Array { func dictionary<Key, Value>(withKey key: KeyPath<Element, Key>, value: KeyPath<Element, Value>) -> [Key: Value] { return reduce(into: [:]) { dictionary, element in let key = element[keyPath: key] let value = element[keyPath: value] dictionary[key] = value } } }
Вот как вы это будете использовать:
struct HTTPHeader { let field: String, value: String } let headers = [ HTTPHeader(field: "Accept", value: "application/json"), HTTPHeader(field: "User-Agent", value: "Safari"), ] let allHTTPHeaderFields = headers.dictionary(withKey: \.field, value: \.value) // allHTTPHeaderFields == ["Accept": "application/json", "User-Agent": "Safari"]
источник
Вы можете использовать функцию уменьшения. Сначала я создал назначенный инициализатор для класса Person
class Person { var name:String var position:Int init(_ n: String,_ p: Int) { name = n position = p } }
Позже я инициализировал массив значений
let myArray = [Person("Bill",1), Person("Steve", 2), Person("Woz", 3)]
И наконец, у словарной переменной есть результат:
let dictionary = myArray.reduce([Int: Person]()){ (total, person) in var totalMutable = total totalMutable.updateValue(person, forKey: total.count) return totalMutable }
источник
Это то, что я использовал
struct Person { let name:String let position:Int } let persons = [Person(name: "Franz", position: 1), Person(name: "Heinz", position: 2), Person(name: "Hans", position: 3)] var peopleByPosition = [Int: Person]() persons.forEach{peopleByPosition[$0.position] = $0}
Было бы неплохо, если бы был способ объединить последние 2 строки, чтобы это
peopleByPosition
могло бытьlet
.Мы могли бы сделать расширение для Array, которое сделает это!
extension Array { func mapToDict<T>(by block: (Element) -> T ) -> [T: Element] where T: Hashable { var map = [T: Element]() self.forEach{ map[block($0)] = $0 } return map } }
Тогда мы можем просто сделать
let peopleByPosition = persons.mapToDict(by: {$0.position})
источник
extension Array { func mapToDict<T>(by block: (Element) -> T ) -> [T: Element] where T: Hashable { var map = [T: Element]() self.forEach{ map[block($0)] = $0 } return map } }
источник
Может как то так?
myArray.forEach({ myDictionary[$0.position] = $0.name })
источник