有没有一种声明式的方法可以将数组转换为字典?
Is there a declarative way to transform Array to Dictionary?
我想从这个字符串数组中得到
let entries = ["x=5", "y=7", "z=10"]
至此
let keyValuePairs = ["x" : "5", "y" : "7", "z" : "10"]
我尝试使用 map
但问题似乎是字典中的键 - 值对不是不同的类型,它只是在我的脑海中,但不是在字典类型中所以我不能'真正提供了一个转换函数,因为没有什么可以转换成的。加上map
return一个数组所以不行
有什么想法吗?
一种方法是分两个阶段使用 map
和 reduce
并使用元组作为中间值,例如:
let entries = ["x=5", "y=7", "z=10"]
let dict = entries.map { (str) -> (String, String) in
let elements = str.characters.split("=").map(String.init)
return (elements[0], elements[1])
}.reduce([String:String]()) { (var dict, kvpair) in
dict[kvpair.0] = kvpair.1
return dict
}
for key in dict.keys {
print("Value for key '\(key)' is \(dict[key]).")
}
输出:
Value for key 'y' is Optional("7").
Value for key 'x' is Optional("5").
Value for key 'z' is Optional("10").
或具有相同输出的单个 reduce
:
let entries = ["x=5", "y=7", "z=10"]
let dict = entries.reduce([String:String]()) { (var dict, entry) in
let elements = entry.characters.split("=").map(String.init)
dict[elements[0]] = elements[1]
return dict
}
for key in dict.keys {
print("Value for key '\(key)' is \(dict[key]).")
}
Swift 4
正如 fl034 所暗示的,这可以通过 Swift 4 进行一些简化,其中错误检查版本如下所示:
let foo = entries
.map { [=10=].components(separatedBy: "=") }
.reduce(into: [String:Int64]()) { dict, pair in
if pair.count == 2, let value = Int64(pair[1]) {
dict[pair[0]] = value
}
}
如果您不想将值作为整数,则更简单:
let foo = entries
.map { [=11=].components(separatedBy: "=") }
.reduce(into: [String:String]()) { dict, pair in
if pair.count == 2 {
dict[pair[0]] = pair[1]
}
}
旧 TL;DR
减去错误检查,它看起来很像:
let foo = entries.map({ [=12=].componentsSeparatedByString("=") })
.reduce([String:Int]()) { acc, comps in
var ret = acc
ret[comps[0]] = Int(comps[1])
return ret
}
使用 map 将 [String]
拆分为 [[String]]
,然后使用 reduce 从中构建 [String:Int]
的字典。
或者,通过向 Dictionary
添加扩展名:
extension Dictionary {
init(elements:[(Key, Value)]) {
self.init()
for (key, value) in elements {
updateValue(value, forKey: key)
}
}
}
(顺便说一句,这是一个非常有用的扩展,你可以用它来对字典进行很多 map/filter 操作,真的很遗憾它默认不存在)
更简单:
let dict = Dictionary(elements: entries
.map({ [=14=].componentsSeparatedByString("=") })
.map({ ([=14=][0], Int([=14=][1])!)})
)
当然,您也可以将这两个映射调用结合起来,但我更喜欢将各个变换分开。
如果要添加一些错误检查,可以使用flatMap
代替map
:
let dict2 = [String:Int](elements: entries
.map({ [=15=].componentsSeparatedByString("=") })
.flatMap({
if [=15=].count == 2, let value = Int([=15=][1]) {
return ([=15=][0], value)
} else {
return nil
}})
)
同样,如果需要,您显然可以将 map
合并到 flatMap
中,或者为简单起见将它们拆分。
let dict2 = [String:Int](elements: entries.flatMap {
let parts = [=16=].componentsSeparatedByString("=")
if parts.count == 2, let value = Int(parts[1]) {
return (parts[0], value)
} else {
return nil
}}
)
我喜欢@paulgriffiths 的回答,但如果您极度厌恶运行时错误,您可以更进一步,以确保初始数组中的每个字符串实际上都具有两个必需的元素...
这段代码与其他代码相比的重要区别在于,我检查以确保字符串中确实有一个“=”,两边都有元素。 flatMap
有效过滤掉所有失败的内容。
extension Dictionary {
func withUpdate(key: Key, value: Value) -> Dictionary<Key, Value> {
var result = self
result[key] = value
return result
}
}
let entries = ["x=5", "y=7", "z=10"]
let keyValues = entries.flatMap { str -> (String, String)? in
let split = str.characters.split("=").map(String.init)
return split.count > 1 ? (split[0], split[1]) : nil
}
let keyValuePairs = keyValues.reduce([String: String]()) { [=10=].withUpdate(.0, value: .1) }
还有其他喜欢超载和单行的人。
public func +<K, V>(lhs: [K:V], rhs: [K:V]) -> [K:V] {
var lhs: [K:V] = lhs
for (key, value) in rhs {
lhs[key] = value
}
return lhs
}
let array = ["x=5", "y=7", "z=10"]
let dictionary = array.map({ [=10=].componentsSeparatedByString("=") }).reduce([:]) { [=10=] + [[0]: [1]] }
print(dictionary) // ["y": "7", "x": "5", "z": "10"]
已经很好的答案了。这是使用集合类型扩展的另一种方法。您可以将任何集合类型转换为字典、数组或集合。
extension CollectionType {
func map2Dict<K, V>(@noescape map: ((Self.Generator.Element) -> (K, V)?)) -> [K: V] {
var d = [K: V]()
for e in self {
if let kV = map(e) {
d[kV.0] = kV.1
}
}
return d
}
func map2Array<T>(@noescape map: ((Self.Generator.Element) -> (T)?)) -> [T] {
var a = [T]()
for e in self {
if let o = map(e) {
a.append(o)
}
}
return a
}
func map2Set<T>(@noescape map: ((Self.Generator.Element) -> (T)?)) -> Set<T> {
return Set(map2Array(map))
}
}
这是示例用法。
let entries = ["x=5", "y=7", "z=10"]
let dict:[String: String] = entries.map2Dict({
let components = [=11=].componentsSeparatedByString("=")
return (components.first!, components.last!)
})
print("dict \(dict)")
如果你有两个并行数组,你可以使用zip()
、Array()
和:
let numbers = [10, 9, 8]
let strings = ["one", "two", "three", "four"]
let dictionary = Dictionary(elements: Array(zip(numbers, strings)))
// == [10: "one", 9: "two", 8: "three"]
不要忘记添加扩展名:
extension Dictionary {
init(elements:[(Key, Value)]) {
self.init()
for (key, value) in elements {
updateValue(value, forKey: key)
}
}
}
简单方法:
let array = ["key1", "key2", "key3"]
let dict = array.flatMap({["\([=10=])" : "value"]})
输出:
[(key: "key1", value: "value"), (key: "key2", value: "value"), (key:
"key3", value: "value")]
更复杂一点的方式:
let array = ["key1", "key2", "key3"]
let dict = array.enumerated().flatMap({["\([=11=].element)" : "value\([=11=].offset)"]})
输出:
[(key: "key1", value: "value0"), (key: "key2", value: "value1"), (key:
"key3", value: "value2")]
使用Swift 4的新reduce(into: Result)
方法:
let keyValuePairs = entries.reduce(into: [String:String]()) { (dict, entry) in
let key = String(entry.first!)
let value = String(entry.last!)
dict[entry.first!] = entry.last!
}
当然,您的字符串拆分可以改进。
我想从这个字符串数组中得到
let entries = ["x=5", "y=7", "z=10"]
至此
let keyValuePairs = ["x" : "5", "y" : "7", "z" : "10"]
我尝试使用 map
但问题似乎是字典中的键 - 值对不是不同的类型,它只是在我的脑海中,但不是在字典类型中所以我不能'真正提供了一个转换函数,因为没有什么可以转换成的。加上map
return一个数组所以不行
有什么想法吗?
一种方法是分两个阶段使用 map
和 reduce
并使用元组作为中间值,例如:
let entries = ["x=5", "y=7", "z=10"]
let dict = entries.map { (str) -> (String, String) in
let elements = str.characters.split("=").map(String.init)
return (elements[0], elements[1])
}.reduce([String:String]()) { (var dict, kvpair) in
dict[kvpair.0] = kvpair.1
return dict
}
for key in dict.keys {
print("Value for key '\(key)' is \(dict[key]).")
}
输出:
Value for key 'y' is Optional("7").
Value for key 'x' is Optional("5").
Value for key 'z' is Optional("10").
或具有相同输出的单个 reduce
:
let entries = ["x=5", "y=7", "z=10"]
let dict = entries.reduce([String:String]()) { (var dict, entry) in
let elements = entry.characters.split("=").map(String.init)
dict[elements[0]] = elements[1]
return dict
}
for key in dict.keys {
print("Value for key '\(key)' is \(dict[key]).")
}
Swift 4
正如 fl034 所暗示的,这可以通过 Swift 4 进行一些简化,其中错误检查版本如下所示:
let foo = entries
.map { [=10=].components(separatedBy: "=") }
.reduce(into: [String:Int64]()) { dict, pair in
if pair.count == 2, let value = Int64(pair[1]) {
dict[pair[0]] = value
}
}
如果您不想将值作为整数,则更简单:
let foo = entries
.map { [=11=].components(separatedBy: "=") }
.reduce(into: [String:String]()) { dict, pair in
if pair.count == 2 {
dict[pair[0]] = pair[1]
}
}
旧 TL;DR
减去错误检查,它看起来很像:
let foo = entries.map({ [=12=].componentsSeparatedByString("=") })
.reduce([String:Int]()) { acc, comps in
var ret = acc
ret[comps[0]] = Int(comps[1])
return ret
}
使用 map 将 [String]
拆分为 [[String]]
,然后使用 reduce 从中构建 [String:Int]
的字典。
或者,通过向 Dictionary
添加扩展名:
extension Dictionary {
init(elements:[(Key, Value)]) {
self.init()
for (key, value) in elements {
updateValue(value, forKey: key)
}
}
}
(顺便说一句,这是一个非常有用的扩展,你可以用它来对字典进行很多 map/filter 操作,真的很遗憾它默认不存在)
更简单:
let dict = Dictionary(elements: entries
.map({ [=14=].componentsSeparatedByString("=") })
.map({ ([=14=][0], Int([=14=][1])!)})
)
当然,您也可以将这两个映射调用结合起来,但我更喜欢将各个变换分开。
如果要添加一些错误检查,可以使用flatMap
代替map
:
let dict2 = [String:Int](elements: entries
.map({ [=15=].componentsSeparatedByString("=") })
.flatMap({
if [=15=].count == 2, let value = Int([=15=][1]) {
return ([=15=][0], value)
} else {
return nil
}})
)
同样,如果需要,您显然可以将 map
合并到 flatMap
中,或者为简单起见将它们拆分。
let dict2 = [String:Int](elements: entries.flatMap {
let parts = [=16=].componentsSeparatedByString("=")
if parts.count == 2, let value = Int(parts[1]) {
return (parts[0], value)
} else {
return nil
}}
)
我喜欢@paulgriffiths 的回答,但如果您极度厌恶运行时错误,您可以更进一步,以确保初始数组中的每个字符串实际上都具有两个必需的元素...
这段代码与其他代码相比的重要区别在于,我检查以确保字符串中确实有一个“=”,两边都有元素。 flatMap
有效过滤掉所有失败的内容。
extension Dictionary {
func withUpdate(key: Key, value: Value) -> Dictionary<Key, Value> {
var result = self
result[key] = value
return result
}
}
let entries = ["x=5", "y=7", "z=10"]
let keyValues = entries.flatMap { str -> (String, String)? in
let split = str.characters.split("=").map(String.init)
return split.count > 1 ? (split[0], split[1]) : nil
}
let keyValuePairs = keyValues.reduce([String: String]()) { [=10=].withUpdate(.0, value: .1) }
还有其他喜欢超载和单行的人。
public func +<K, V>(lhs: [K:V], rhs: [K:V]) -> [K:V] {
var lhs: [K:V] = lhs
for (key, value) in rhs {
lhs[key] = value
}
return lhs
}
let array = ["x=5", "y=7", "z=10"]
let dictionary = array.map({ [=10=].componentsSeparatedByString("=") }).reduce([:]) { [=10=] + [[0]: [1]] }
print(dictionary) // ["y": "7", "x": "5", "z": "10"]
已经很好的答案了。这是使用集合类型扩展的另一种方法。您可以将任何集合类型转换为字典、数组或集合。
extension CollectionType {
func map2Dict<K, V>(@noescape map: ((Self.Generator.Element) -> (K, V)?)) -> [K: V] {
var d = [K: V]()
for e in self {
if let kV = map(e) {
d[kV.0] = kV.1
}
}
return d
}
func map2Array<T>(@noescape map: ((Self.Generator.Element) -> (T)?)) -> [T] {
var a = [T]()
for e in self {
if let o = map(e) {
a.append(o)
}
}
return a
}
func map2Set<T>(@noescape map: ((Self.Generator.Element) -> (T)?)) -> Set<T> {
return Set(map2Array(map))
}
}
这是示例用法。
let entries = ["x=5", "y=7", "z=10"]
let dict:[String: String] = entries.map2Dict({
let components = [=11=].componentsSeparatedByString("=")
return (components.first!, components.last!)
})
print("dict \(dict)")
如果你有两个并行数组,你可以使用zip()
、Array()
和
let numbers = [10, 9, 8]
let strings = ["one", "two", "three", "four"]
let dictionary = Dictionary(elements: Array(zip(numbers, strings)))
// == [10: "one", 9: "two", 8: "three"]
不要忘记添加扩展名:
extension Dictionary {
init(elements:[(Key, Value)]) {
self.init()
for (key, value) in elements {
updateValue(value, forKey: key)
}
}
}
简单方法:
let array = ["key1", "key2", "key3"]
let dict = array.flatMap({["\([=10=])" : "value"]})
输出:
[(key: "key1", value: "value"), (key: "key2", value: "value"), (key: "key3", value: "value")]
更复杂一点的方式:
let array = ["key1", "key2", "key3"]
let dict = array.enumerated().flatMap({["\([=11=].element)" : "value\([=11=].offset)"]})
输出:
[(key: "key1", value: "value0"), (key: "key2", value: "value1"), (key: "key3", value: "value2")]
使用Swift 4的新reduce(into: Result)
方法:
let keyValuePairs = entries.reduce(into: [String:String]()) { (dict, entry) in
let key = String(entry.first!)
let value = String(entry.last!)
dict[entry.first!] = entry.last!
}
当然,您的字符串拆分可以改进。