将对象数组映射到Swift中的Dictionary

我有一个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 } 

有没有任何优雅的方式由Swift用function方法mapflatMap …或其他现代Swift风格来做到这一点

好的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您可以使用reduce版本更干净,更有效地执行@ Tj3n的方法。它删除了临时字典和返回值,因此它更快更容易阅读。

 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)] let myDict = myArray.reduce(into: [Int: String]()) { $0[$1.position] = $1.name } print(myDict) 

注意:在Swift 3中不起作用。

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)]

您可以为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)}) 

您可以使用reducefunction。 首先,我为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} 

如果有一种方法可以组合最后两行,那么peopleByPosition可以是一个letpeopleByPosition了。

我们可以扩展到Array那样做!

 extension Array { func mapToDict(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})