是否有将Array转换为Dictionary的声明方法?

时间:2016-02-21 12:18:45

标签: swift declarative

我想从这个字符串数组中获取

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

到这个

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

我尝试使用map,但问题似乎是字典中的键值对不是一个独特的类型,它只是在我的脑海中,而不是在字典类型中所以我不能真的提供了一个转换函数,因为没有什么可以转换的。加map返回一个数组,这样就不行了。

有什么想法吗?

8 个答案:

答案 0 :(得分:52)

Swift 4

正如fl034所提到的,这可以通过Swift 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
        }
    }

如果你不希望这些值成为Ints,那就更简单了:

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

减去错误检查,它看起来非常像:

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]],然后使用reduce构建[String:Int]字典。

或者,通过向Dictionary添加扩展程序:

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

(相当有用的扩展顺便说一句,你可以用它来进行字典上的很多地图/过滤操作,真的很遗憾它默认不存在)

变得更加简单:

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

当然,您也可以将两个地图调用结合起来,但我更愿意分解各个变换。

如果您想添加一些错误检查,可以使用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
        }})
)

同样,如果您愿意,您可以明显将map合并到flatMap中,或者为了简单起见将其拆分。

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

答案 1 :(得分: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]).")
}

答案 2 :(得分:8)

使用 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!
}

当然,可以改进字符串的拆分。

答案 3 :(得分: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)")

答案 4 :(得分: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) }

答案 5 :(得分:0)

对于喜欢超载和单行的其他人。

jQuery(document).ready(function($) {
    $('.exampleimg').click(function() {
        $('.exampleimg').removeClass('exampleimgopen').addClass('exampleimgclose');    
        $(this).addClass('exampleimgopen').removeClass('exampleimgclose')
    });
});

答案 6 :(得分: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)
        }
    }
}

答案 7 :(得分:0)

简单方法:

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

输出:

  

[(键:&#34;键1和#34;,值:&#34;值&#34;),(键:&#34;键2&#34;,值:&#34;值&#34; ),(关键:   &#34; key3&#34;,值:&#34;值&#34;)]

更复杂的方式:

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

输出:

  

[(键:&#34; key1&#34;,值:&#34; value0&#34;),(键:&#34;键2&#34;,值:&#34; value1&#34; ),(关键:   &#34; key3&#34;,值:&#34; value2&#34;)]