将map()应用到Swift中的字典最干净的方法是什么?
我想在字典中的所有键上映射一个函数。 我希望像下面这样的工作,但filter不能直接应用于字典。 什么是最简单的方法来实现这一点?
在这个例子中,我试图用1递增每个值。然而,这是偶然的例子 – 主要目的是弄清楚如何将map()应用到字典中。
var d = ["foo" : 1, "bar" : 2] d.map() { $0.1 += 1 }
斯威夫特4
好消息! Swift 4包含一个mapValues(_:)
方法,它使用相同的键,但不同的值构造一个字典的副本。 它还包括一个返回Dictionary
的filter(_:)
重载,以及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
map
是Sequence
和Collection
的扩展方法。 它是这样定义的:
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
,其中函数keyTransform
和valueTransform
是函数。
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)
参考