将对象数组映射到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方法map
, flatMap
…或其他现代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
可以是一个let
, peopleByPosition
了。
我们可以扩展到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})