将map()应用于Swift中的字典最简洁的方法是什么?

Mar*_*ina 135 dictionary swift

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

在这个例子中,我试图将每个值递增1.但这对于示例来说是偶然的 - 主要目的是弄清楚如何将map()应用于字典.

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

d.map() {
    $0.1 += 1
}
Run Code Online (Sandbox Code Playgroud)

Bre*_*don 254

斯威夫特4

好消息!Swift 4包含一种mapValues(_:)方法,该方法使用相同的键但不同的值构造字典的副本.它还包括一个filter(_:)过载它返回一个Dictionary,以及init(uniqueKeysWithValues:)init(_:uniquingKeysWith:)初始化来创建一个Dictionary从元组的任意序列.这意味着,如果您想要更改键和值,您可以这样说:

let newDict = Dictionary(uniqueKeysWithValues:
    oldDict.map { key, value in (key.uppercased(), value.lowercased()) })
Run Code Online (Sandbox Code Playgroud)

还有用于将字典合并在一起的新API,用缺省元素替换缺省值,对值进行分组(将集合转换为数组字典,通过将集合映射到某个函数的结果来键入)等等.

在讨论引入这些功能的SE-0165提案时,我多次提出这个Stack Overflow的答案,我认为大量的upvotes有助于证明需求.谢谢你的帮助让Swift变得更好!

斯威夫特3

mapValues(_:)是一个扩展方法filter(_:)Dictionary.它的定义如下:

let newDict = Dictionary(uniqueKeysWithValues:
    oldDict.map { key, value in (key.uppercased(), value.lowercased()) })
Run Code Online (Sandbox Code Playgroud)

由于init(uniqueKeysWithValues:)符合init(_:uniquingKeysWith:),并且其Dictionary类型是(键,值)元组,这意味着您应该能够执行以下操作:

let newDict = Dictionary(uniqueKeysWithValues:
    oldDict.map { key, value in (key.uppercased(), value.lowercased()) })
Run Code Online (Sandbox Code Playgroud)

但是,这实际上不会分配给mapValues(_:)-typed变量.该filter(_:)方法被定义为始终返回一个数组(the Dictionary),即使对于其他类型的字典也是如此.如果你编写一个构造函数,它将把一个二元组的数组转换为一个,init(uniqueKeysWithValues:)并且所有这些构造函数都适用于这个世界:

let newDict = Dictionary(uniqueKeysWithValues:
    oldDict.map { key, value in (key.uppercased(), value.lowercased()) })
Run Code Online (Sandbox Code Playgroud)

您甚至可能希望编写特定于字典的版本,init(_:uniquingKeysWith:)以避免显式调用构造函数.在这里,我还包括了一个实现Dictionary:

let newDict = Dictionary(uniqueKeysWithValues:
    oldDict.map { key, value in (key.uppercased(), value.lowercased()) })
Run Code Online (Sandbox Code Playgroud)

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

像这样使用它:

let newDict = Dictionary(uniqueKeysWithValues:
    oldDict.map { key, value in (key.uppercased(), value.lowercased()) })
Run Code Online (Sandbox Code Playgroud)

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

let newDict = Dictionary(uniqueKeysWithValues:
    oldDict.map { key, value in (key.uppercased(), value.lowercased()) })
Run Code Online (Sandbox Code Playgroud)

原因是mapValues(_:)如果两个最终映射到同一个键,可能会丢失对.filter(_:)另一方面,总是保留钥匙,所以这不是危险.

  • 更新了Swift 2.1?得到```DictionaryExtension.swift:13:16:参数标签'(_ :)'不匹配任何可用的重载``` (9认同)
  • 这当然是不完美的,但我们绝不能让完美成为善的敌人.在许多情况下,可能产生冲突键的`map`仍然是一个有用的`map`.(另一方面,你可以说只映射值是字典上`map`的自然解释 - 原始海报的例子和我只修改了值,而在概念上,数组上的`map`只修改了值,而不是指数.) (5认同)
  • 使用Swift 2.2我在实现最后一个扩展时得到`Argument labels'(_ :)'与任何可用的重载都不匹配.不确定如何解决它:/ (4认同)
  • 你的最后一个代码块似乎缺少一个`try`:`return Dictionary <Key,OutValue>(试试map {(k,v)in(k,try transform(v))})` (2认同)
  • @Audioy该代码片段取决于前面一个示例中添加的`Dictionary`初始化程序.确保包括它们. (2认同)
  • @Nicolas Miari:`compactMapValues(_:)` 已被 Swift 5 接受,因此它在当前测试版的 Xcode 版本中不可用,但它将在下一个主要版本中提供。 (2认同)

Ima*_*tit 41

使用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]
Run Code Online (Sandbox Code Playgroud)

#2.使用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]
Run Code Online (Sandbox Code Playgroud)

#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]
Run Code Online (Sandbox Code Playgroud)

#4.使用reduce(into:_:) Dictionary下标

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]
Run Code Online (Sandbox Code Playgroud)

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

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]
Run Code Online (Sandbox Code Playgroud)


Air*_*ity 18

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

这是一个扩展名,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)))
    }

}
Run Code Online (Sandbox Code Playgroud)


Jos*_*ark 11

最干净的方法是添加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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

检查它是否有效:

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)
Run Code Online (Sandbox Code Playgroud)

输出:

[c: 71, a: 51, b: 61]
[c: 142, a: 51, b: 122]
Run Code Online (Sandbox Code Playgroud)


Aar*_*sen 7

您也可以使用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]
Run Code Online (Sandbox Code Playgroud)

将它包装在扩展中是相当容易的,但即使没有扩展,它也可以让你通过一个函数调用做你想做的事.

  • 这种方法的缺点是每次添加新密钥时都会生成一个全新的字典副本,因此这个函数非常低效(优化器_might_保存你). (10认同)
  • 不需要 temp var,reduce 现在是 Swift 2.0 中的一个方法: `let newDict = oldDict.reduce([String:Int]()) { (var dict,pair) in dict["new\(pair.1)" ] = 对.1; 返回字典 }` (2认同)

NøB*_*Mac 5

雨燕5

字典的map函数带有这种语法。

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
}
Run Code Online (Sandbox Code Playgroud)

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

然后只需_ =在变量之前设置即可。

也许它会对你有帮助

谢谢。

  • 不要使用 .map 来打印某些内容,请使用 .map 将 A 转换为 B。如果需要打印,请使用 .foreach。这不是问题的答案 (10认同)