2016-02-21 46 views
27

我想从这个字符串数组是否有将数组转换为字典的声明方式?

let entries = ["x=5", "y=7", "z=10"] 

的去这个

let keyValuePairs = ["x" : "5", "y" : "7", "z" : "10"] 

我试图用map但问题似乎是一个关键 - 在字典中的值对是不是不同的类型,它只是在我的脑海中,但不是在Dictionary类型中,所以我不能提供一个转换函数,因为没有任何转换。加map返回一个数组,所以这是一个不行。

任何想法?

+0

如果数组中的字符串总是采用相同的格式,那么可以解析键值“=”和值,并将所述数据分配给字典。 – Arc676

+0

@ Arc676这是一种必要的方法,当然,我已经拥有了它......但我正在寻找一种声明性解决方案。 –

回答

47

斯威夫特4

正如fl034提到的,这可以用斯威夫特4将错误选中的版本看起来像简化一些:

let foo = entries 
    .map { $0.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 { $0.components(separatedBy: "=") } 
    .reduce(into: [String:String]()) { dict, pair in 
     if pair.count == 2 { 
      dict[pair[0]] = pair[1] 
     } 
    } 

旧版TL; DR

减号错误检查,它厕所KS很像:

let foo = entries.map({ $0.componentsSeparatedByString("=") }) 
    .reduce([String:Int]()) { acc, comps in 
     var ret = acc 
     ret[comps[0]] = Int(comps[1]) 
     return ret 
    } 

使用地图转[String]陷入分裂了[[String]],然后构建的[String:Int]字典从使用减少。

或者,通过添加扩展Dictionary

extension Dictionary { 
    init(elements:[(Key, Value)]) { 
     self.init() 
     for (key, value) in elements { 
      updateValue(value, forKey: key) 
     } 
    } 
} 

(相当有用的扩展顺便说一句,你可以用它来了很多字典映射/过滤操作,真有种耻辱它doesn”牛逼存在默认)

它变得更简单:

let dict = Dictionary(elements: entries 
    .map({ $0.componentsSeparatedByString("=") }) 
    .map({ ($0[0], Int($0[1])!)}) 
) 

当然,你也可以将二者结合起来的地图调用,但我更愿意分手的individu al转换。

如果你想添加一些错误检查,flatMap可以用来代替的map

let dict2 = [String:Int](elements: entries 
    .map({ $0.componentsSeparatedByString("=") }) 
    .flatMap({ 
     if $0.count == 2, let value = Int($0[1]) { 
      return ($0[0], value) 
     } else { 
      return nil 
     }}) 
) 

同样,如果你愿意,你可以明显地合并mapflatMap或将它们分割为简单起见。

let dict2 = [String:Int](elements: entries.flatMap { 
    let parts = $0.componentsSeparatedByString("=") 
    if parts.count == 2, let value = Int(parts[1]) { 
     return (parts[0], value) 
    } else { 
     return nil 
    }} 
) 
+0

出于好奇,“acc”,“comps”和“ret”的缩写是什么? –

+2

accumulatior,组件,并返回 –

+0

看看我的答案,因为斯威夫特4使这一切过时 – fl034

9

一种方式做到这一点是在两个阶段中与mapreduce与一个元组作为中间值,例如:

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]).") 
} 
+0

非常好。请注意,'str.characters.split {$ 0 ==“=”}'也可以写成'str.characters.split(“=”)' –

+0

也是在第一个地图闭包中,你不需要声明var是'str'参数。它可以简单地为'entries.map {(str) - >(String,String)' –

+1

@appzYourLife:好点,编辑,谢谢。 –

1

我喜欢@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]()) { $0.withUpdate($1.0, value: $1.1) } 
0

而对于其他所有享受超载和单线的人来说。

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({ $0.componentsSeparatedByString("=") }).reduce([:]) { $0 + [$1[0]: $1[1]] } 

print(dictionary) // ["y": "7", "x": "5", "z": "10"] 
3

已经有很好的答案了。这是集合类型扩展的另一种方式。您可以将任何集合类型转换为字典,数组或集合。

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 = $0.componentsSeparatedByString("=") 
    return (components.first!, components.last!) 
}) 

print("dict \(dict)") 
+0

这创建了一个新的数组,并附加到它,不是很有用,也不能懒惰。 –

0

如果你有两个平行阵列,可以使用zip()Array()David Berry's Dictionary extension

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

简单的方法:

let array = ["key1", "key2", "key3"] 
let dict = array.flatMap({["\($0)" : "value"]}) 

输出:

[(键: “KEY1”,值: “值”),(键: “KEY2”,值: “值”),(关键: “KEY3”,值:“值“)]


稍微复杂的方法:

let array = ["key1", "key2", "key3"] 
let dict = array.enumerated().flatMap({["\($0.element)" : "value\($0.offset)"]}) 

输出:

[(key:“key1”,value:“value0”),(key:“key2”,value:“value1”),(key: “key3”,value:“value2”)]

5

使用斯威夫特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! 
} 

当然,你的字符串的分割可以改善。

相关问题