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

我想映射字典中所有键的函数。我希望像下面这样的东西可以工作,但是过滤器不能直接应用于字典。实现这一目标的最干净的方式是什么?

在这个例子中,我试图将每个值增加1。然而,对于这个示例来说,这是附带的——其主要目的是弄清楚如何将 map ()应用到字典中。

var d = ["foo" : 1, "bar" : 2]


d.map() {
$0.1 += 1
}
136524 次浏览

根据 Swift 标准库参考资料,map 是数组的函数,而不是字典的函数。

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

var d = ["foo" : 1, "bar" : 2]


for (name, key) in d {
d[name] = d[name]! + 1
}

事实证明你可以做到。您需要做的是从字典 keys方法返回的 MapCollectionView<Dictionary<KeyType, ValueType>, KeyType>中创建一个数组。(这里有信息)然后您可以映射这个数组,并将更新后的值传递回字典。

var dictionary = ["foo" : 1, "bar" : 2]


Array(dictionary.keys).map() {
dictionary.updateValue(dictionary[$0]! + 1, forKey: $0)
}


dictionary

最简单的方法是将 map添加到 Dictionary:

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]

Swift 4 +

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

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

还有新的 API 用于合并字典,替换缺少的元素的默认值,分组值(将集合转换为数组的字典,由映射集合到某个函数的结果键控) ,等等。

在讨论引入了这些特性的提案 SE-0165时,我多次提出了 Stack Overflow 的答案,我认为绝对数量的赞成票有助于证明需求。所以谢谢你帮我让斯威夫特好起来!

我正在寻找一种将字典直接映射到带有自定义对象的类型化数组的方法。在这个扩展中找到了解决方案:

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

你也可以使用 reduce代替地图。 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]

用扩展包装它是相当容易的,但是即使没有扩展,它也允许您用一个函数调用来做您想做的事情。

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

这里有一个扩展 mapValues,它允许您只映射这些值。注意,它还使用键/值对序列中的 init扩展 dictionary,这比从数组中初始化它要通用一些:

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转换为字典和 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
}

Swift 3

用法:

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

分机:

extension Dictionary {
func transformed(closure: (Key, Value) -> (Key, Value)?) -> [Key: Value] {
var dict = [Key: Value]()


for key in keys {
guard
let value = self[key],
let keyValue = closure(key, value)
else { continue }
                

dict[keyValue.0] = keyValue.1
}


return dict
}
}

Swift 3

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

我想映射 [ String: String? ][字符串: 字符串],我使用 for 而不是 map 或者平面 map。

    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
}
}

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

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": ...

我用了这个,

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)

裁判

有了 Swift 5,你可以使用其中一个 接下来的五个片段来解决你的问题。


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

使用 Dictionary map方法和 init(uniqueKeysWithValues:)初始化器

let dictionary = ["foo": 1, "bar": 2, "baz": 5]


let tupleArray = dictionary.map { (key: String, value: Int) in
return (key, value + 1)
}
//let tupleArray = dictionary.map { ($0, $1 + 1) } // also works


let newDictionary = Dictionary(uniqueKeysWithValues: tupleArray)


print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

# 3. 使用 Dictionary reduce(_:_:)方法或 reduce(into:_:)方法

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]
let dictionary = ["foo": 1, "bar": 2, "baz": 5]


let newDictionary = dictionary.reduce(into: [:]) { (result: inout [String: Int], tuple: (key: String, value: Int)) in
result[tuple.key] = tuple.value + 1
}


print(newDictionary) // prints: ["baz": 6, "foo": 2, "bar": 3]

使用 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]

使用 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 5

Dictionary 的映射函数就是这种语法。

dictData.map(transform: ((key: String, value: String)) throws -> T)

可以将闭包值设置为

var dictData: [String: String] = [
"key_1": "test 1",
"key_2": "test 2",
"key_3": "test 3",
"key_4": "test 4",
"key_5": "test 5",
]
        

dictData.map { (key, value) in
// Operations on values or key
}

它可能会给这个警告 Result of call to 'map' is unused

然后在变量之前设置 _ =

也许能帮到你

谢谢你。