web-dev-qa-db-ja.com

配列を辞書に変換する宣言的な方法はありますか?

この文字列の配列から取得したい

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

これに

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

私はmapを使用しようとしましたが、問題は辞書のキーと値のペアが明確なタイプではないようです、それはちょうど私の心にありますが、辞書タイプではないので、本当に提供できませんでした変換するものがないため、変換関数。さらに、mapは配列を返すので、手間がかかりません。

何か案は?

31
Earl Grey

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
    }

Mapを使用して[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])!)})
)

もちろん、2つのマップ呼び出しを組み合わせることもできますが、個々の変換を分割することを好みます。

エラーチェックを追加する場合は、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
    }}
)
52
David Berry

その方法の1つは、中間値としてTupleを使用するmapreduceの2段階です。たとえば、

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]).")
}
9
Paul Griffiths

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

もちろん、文字列の分割は改善できます。

8
fl034

すでに良い答えです。コレクションタイプの拡張機能を使用した追加の方法を次に示します。任意のコレクションタイプをディクショナリ、配列、またはセットに変換できます。

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)")
3
user3255356

私は@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) }
1
Daniel T.

簡単な方法:

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

出力:

[(key: "key1"、value: "value")、(key: "key2"、value: "value")、(key: "key3"、value: "value")]


もう少し複雑な方法:

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")]

0
Dmitrii Z

そして、過負荷とワンライナーを楽しむすべての人のために。

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"]
0
Ian Bytchek

2つの並列配列がある場合は、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
Senseful