将map()应用到Swift中的字典最干净的方法是什么?

我想在字典中的所有键上映射一个函数。 我希望像下面这样的工作,但filter不能直接应用于字典。 什么是最简单的方法来实现这一点?

在这个例子中,我试图用1递增每个值。然而,这是偶然的例子 – 主要目的是弄清楚如何将map()应用到字典中。

var d = ["foo" : 1, "bar" : 2] d.map() { $0.1 += 1 } 

斯威夫特4

好消息! Swift 4包含一个mapValues(_:)方法,它使用相同的键,但不同的值构造一个字典的副本。 它还包括一个返回Dictionaryfilter(_:)重载,以及init(uniqueKeysWithValues:)init(_:uniquingKeysWith:)初始值设定项,以从任意元组序列创build一个Dictionary 。 这意味着,如果你想改变键和值,你可以这样说:

 let newDict = Dictionary(uniqueKeysWithValues: oldDict.map { key, value in (key.uppercased(), value.lowercased()) }) 

还有一些新的API将字典合并在一起,将默认值replace为缺less的元素,对值进行分组(将集合转换为数组字典,将集合映射到某个函数的结果)。

在讨论提案SE-0165时,介绍了这些特性,我多次提出了这个Stack Overflow的答案,我认为上升的数量有助于certificate需求。 所以谢谢你的帮助让Swift更好!

Swift 3

mapSequenceCollection的扩展方法。 它是这样定义的:

 func map<T>(_ transform: (Iterator.Element) throws -> T) rethrows -> [T] 

既然Dictionary符合Collection ,而且它的Element typealias是一个(key,value)元组,那么就意味着你应该可以这样做:

 result = dict.map { (key, value) in (key, value.uppercaseString) } 

但是,这实际上不会分配给Dictionary typevariables。 map方法被定义为总是返回一个数组( [T] ),即使是像字典这样的其他types。 如果你写一个构造函数,将一个二元组的数组转换成一个Dictionary ,所有的东西都是正确的:

 extension Dictionary { init(_ pairs: [Element]) { self.init() for (k, v) in pairs { self[k] = v } } } result = Dictionary(dict.map { (key, value) in (key, value.uppercaseString) }) 

你甚至可能想编写一个特定于字典的map版本,以避免显式调用构造函数。 在这里我还包含了一个filter的实现:

 extension Dictionary { func mapPairs<OutKey: Hashable, OutValue>(_ transform: (Element) throws -> (OutKey, OutValue)) rethrows -> [OutKey: OutValue] { return Dictionary<OutKey, OutValue>(try map(transform)) } func filterPairs(_ includeElement: (Element) throws -> Bool) rethrows -> [Key: Value] { return Dictionary(try filter(includeElement)) } } 

我使用了不同的名称,否则只有返回值会区分这两个方法,这会使代码非常模糊。

像这样使用它:

 result = dict.mapPairs { (key, value) in (key, value.uppercaseString) } 

顺便说一下,对于许多目的,您可能只想映射值:

 extension Dictionary { func map<OutValue>(_ transform: (Value) throws -> OutValue) rethrows -> [Key: OutValue] { return Dictionary<Key, OutValue>(try map { (k, v) in (k, try transform(v)) }) } } 

原因是,如果两个映射到同一个键, mapPairs可能会丢失对。 map(Value -> OutValue) ,另一方面,总是保留的钥匙,所以这不是一个危险。

虽然这里的大部分答案都关注如何映射整个字典(键值),但问题实际上只是想要映射值。 这是一个重要的区别,因为映射值允许您保证相同数量的条目,而映射键和值都可能导致重复的键。

这里有一个扩展mapValues ,它允许你只映射值。 请注意,它也使用一系列键/值对的init来扩展字典,这比从数组中初始化它更普遍一些:

 extension Dictionary { init<S: SequenceType where S.Generator.Element == Element> (_ seq: S) { self.init() for (k,v) in seq { self[k] = v } } func mapValues<T>(transform: Value->T) -> Dictionary<Key,T> { return Dictionary<Key,T>(zip(self.keys, self.values.map(transform))) } } 

最简洁的方法是将map添加到词典中:

 extension Dictionary { mutating func map(transform: (key:KeyType, value:ValueType) -> (newValue:ValueType)) { for key in self.keys { var newValue = transform(key: key, value: self[key]!) self.updateValue(newValue, forKey: key) } } } 

检查它的工作原理:

 var dic = ["a": 50, "b": 60, "c": 70] dic.map { $0.1 + 1 } println(dic) dic.map { (key, value) in if key == "a" { return value } else { return value * 2 } } println(dic) 

输出:

 [c: 71, a: 51, b: 61] [c: 142, a: 51, b: 122] 

你也可以使用reduce而不是map。 reduce是能够做任何事情map可以做和更多!

 let oldDict = ["old1": 1, "old2":2] let newDict = reduce(oldDict, [String:Int]()) { dict, pair in var d = dict d["new\(pair.1)"] = pair.1 return d } println(newDict) // ["new1": 1, "new2": 2] 

将这个扩展包装起来相当容易,但是即使没有扩展,它也可以让你用一个函数调用来做你想做的事情。

事实certificate,你可以做到这一点。 你需要做的是从字典keys方法返回的MapCollectionView<Dictionary<KeyType, ValueType>, KeyType>创build一个数组。 ( Info here )然后,您可以映射此数组,并将更新的值传递回字典。

 var dictionary = ["foo" : 1, "bar" : 2] Array(dictionary.keys).map() { dictionary.updateValue(dictionary[$0]! + 1, forKey: $0) } dictionary 

使用Swift 4,您可以使用以下五个片段之一来解决您的问题。


#1。 使用Dictionary mapValues(_:)方法

 let dictionary = ["foo": 1, "bar": 2, "baz": 5] let newDictionary = dictionary.mapValues { value in return value + 1 } //let newDictionary = dictionary.mapValues { $0 + 1 } // also works print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3] 

#2。 使用Dictionary map方法和init(uniqueKeysWithValues:)初始值设定项

 let dictionary = ["foo": 1, "bar": 2, "baz": 5] let tupleArray = dictionary.map { (tuple: (key: String, value: Int)) in return (tuple.key, tuple.value + 1) } // let tupleArray = dictionary.map { ($0.0, $0.1 + 1) } // also works let newDictionary = Dictionary(uniqueKeysWithValues: tupleArray) print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3] 

#3。 使用Dictionary reduce(_:_:)方法

 let dictionary = ["foo": 1, "bar": 2, "baz": 5] let newDictionary = dictionary.reduce([:]) { (partialResult: [String: Int], tuple: (key: String, value: Int)) in var result = partialResult result[tuple.key] = tuple.value + 1 return result } print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3] 

#4。 使用Dictionary subscript(_:default:)下标

 let dictionary = ["foo": 1, "bar": 2, "baz": 5] var newDictionary = [String: Int]() for (key, value) in dictionary { newDictionary[key, default: value] += 1 } print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3] 

#5。 使用Dictionary subscript(_:)下标

 let dictionary = ["foo": 1, "bar": 2, "baz": 5] var newDictionary = [String: Int]() for (key, value) in dictionary { newDictionary[key] = value + 1 } print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3] 

根据Swift标准库参考,map是一个数组的函数。 不适用于字典。

但是你可以迭代你的字典来修改键值:

 var d = ["foo" : 1, "bar" : 2] for (name, key) in d { d[name] = d[name]! + 1 } 

我正在寻找一种将字典映射到带有自定义对象的types化数组中的方法。 在这个扩展中find解决scheme:

 extension Dictionary { func mapKeys<U> (transform: Key -> U) -> Array<U> { var results: Array<U> = [] for k in self.keys { results.append(transform(k)) } return results } func mapValues<U> (transform: Value -> U) -> Array<U> { var results: Array<U> = [] for v in self.values { results.append(transform(v)) } return results } func map<U> (transform: Value -> U) -> Array<U> { return self.mapValues(transform) } func map<U> (transform: (Key, Value) -> U) -> Array<U> { var results: Array<U> = [] for k in self.keys { results.append(transform(k as Key, self[ k ]! as Value)) } return results } func map<K: Hashable, V> (transform: (Key, Value) -> (K, V)) -> Dictionary<K, V> { var results: Dictionary<K, V> = [:] for k in self.keys { if let value = self[ k ] { let (u, w) = transform(k, value) results.updateValue(w, forKey: u) } } return results } } 

使用它如下:

 self.values = values.map({ (key:String, value:NSNumber) -> VDLFilterValue in return VDLFilterValue(name: key, amount: value) }) 

我假设问题是保留原始字典的关键字,并以某种方式映射其所有的值。

让我们概括一下,说我们可能想把所有的值映射到另一种types

所以我们想从一个字典和一个闭包(一个函数)开始,结束一个新的字典。 当然,问题在于Swift严格的打字方式。 closures需要指定什么types进入,哪种types出来,因此我们不能做一个方法,采取普遍的封闭 – 除了在一个通用的情况下。 因此我们需要一个通用的函数。

其他解决scheme已经集中在Dictionarygenerics结构本身的generics世界中,但是我发现从顶层函数的angular度来看更容易。 例如,我们可以这样写,其中K是关键字types,V1是起始字典的值types,V2是结束字典的值types:

 func mapValues<K,V1,V2>(d1:[K:V1], closure:(V1)->V2) -> [K:V2] { var d2 = [K:V2]() for (key,value) in zip(d1.keys, d1.values.map(closure)) { d2.updateValue(value, forKey: key) } return d2 } 

下面是一个调用它的简单例子,只是为了certificategenerics在编译时确实会自行parsing:

 let d : [String:Int] = ["one":1, "two":2] let result = mapValues(d) { (i : Int) -> String in String(i) } 

我们从一个[String:Int]的字典开始,最后用一个[String:String]的字典,通过闭包来转换第一个字典的值。

(编辑:我现在看到,这是实际上是相同的AirspeedVelocity的解决scheme,除了我没有添加一个字典初始化,使一个字典从一个zip序列的额外雅致。

Swift 3

用法:

 let bob = ["a": "AAA", "b": "BBB", "c": "CCC"] bob.mapDictionary { ($1, $0) } // ["BBB": "b", "CCC": "c", "AAA": "a"] 

延期:

 extension Dictionary { func mapDictionary(transform: (Key, Value) -> (Key, Value)?) -> Dictionary<Key, Value> { var dict = [Key: Value]() for key in keys { guard let value = self[key], let keyValue = transform(key, value) else { continue } dict[keyValue.0] = keyValue.1 } return dict } } 

Swift 3

我在Swift 3中尝试一个简单的方法。

我想将[String:String?]映射到[String:String] ,我使用forEach而不是地图或平面地图。

  let oldDict = ["key0": "val0", "key1": nil, "key1": "val2","key2": nil] var newDict = [String: String]() oldDict.forEach { (source: (key: String, value: String?)) in if let value = source.value{ newDict[source.key] = value } } 

另一种方法是map到一个字典并reduce ,其中函数keyTransformvalueTransform是函数。

 let dictionary = ["a": 1, "b": 2, "c": 3] func keyTransform(key: String) -> Int { return Int(key.unicodeScalars.first!.value) } func valueTransform(value: Int) -> String { return String(value) } dictionary.map { (key, value) in [keyTransform(key): valueTransform(value)] }.reduce([Int:String]()) { memo, element in var m = memo for (k, v) in element { m.updateValue(v, forKey: k) } return m } 

我只是试图映射值 (可能改变他们的types),包括这个扩展名:

 extension Dictionary { func valuesMapped<T>(_ transform: (Value) -> T) -> [Key: T] { var newDict = [Key: T]() for (key, value) in self { newDict[key] = transform(value) } return newDict } } 

鉴于你有这个字典:

 let intsDict = ["One": 1, "Two": 2, "Three": 3] 

单行值转换则如下所示:

 let stringsDict = intsDict.valuesMapped { String($0 * 2) } // => ["One": "2", "Three": "6", "Two": "4"] 

多行值转换看起来像这样:

 let complexStringsDict = intsDict.valuesMapped { (value: Int) -> String in let calculationResult = (value * 3 + 7) % 5 return String("Complex number #\(calculationResult)") } // => ["One": "Complex number #0", "Three": ... 

Swift 3我用这个,

 func mapDict(dict:[String:Any])->[String:String]{ var updatedDict:[String:String] = [:] for key in dict.keys{ if let value = dict[key]{ updatedDict[key] = String(describing: value) } } return updatedDict } 

用法:

 let dict:[String:Any] = ["MyKey":1] let mappedDict:[String:String] = mapDict(dict: dict) 

参考