Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift dictionary comprehension

Other languages such as Python let you use a dictionary comprehension to make a dict from an array, but I haven't figure out how to do this in Swift. I thought I could use something like this but it doesn't compile:

let x = ["a","b","c"]
let y = x.map( { ($0:"x") })
// expected y to be ["a":"x", "b":"x", "c":"x"]

What is the correct way to generate a dictionary from an array in swift?

like image 286
Yusuf X Avatar asked Aug 15 '15 06:08

Yusuf X


1 Answers

The map method simply transforms each element of an array into a new element. The result is, however, still an array. To transform the array into a dictionary you can use the reduce method.

let x = ["a","b","c"]
let y = x.reduce([String: String]()) { (var dict, arrayElem) in
    dict[arrayElem] = "this is the value for \(arrayElem)"
    return dict
}

This will generate the dictionary

["a": "this is the value for a",
 "b": "this is the value for b",
 "c": "this is the value for c"]

Some explanation: The first argument of reduce is the initial value which in this case is the empty dictionary [String: String](). The second argument of reduce is a callback for combining each element of the array into the current value. In this case, the current value is the dictionary and we define a new key and value in it for every array element. The modified dictionary also needs to be returned in the callback.


Update: Since the reduce approach can be heavy on memory for large arrays (see comments) you could also define a custom comprehension function similar to the below snippet.

func dictionaryComprehension<T,K,V>(array: [T], map: (T) -> (key: K, value: V)?) -> [K: V] {
    var dict = [K: V]()
    for element in array {
        if let (key, value) = map(element) {
            dict[key] = value
        }
    }
    return dict
}

Calling that function would look like this.

let x = ["a","b","c"]
let y = dictionaryComprehension(x) { (element) -> (key: String, value: String)? in
    return (key: element, value: "this is the value for \(element)")
}

Update 2: Instead of a custom function you could also define an extension on Array which would make the code easier to reuse.

extension Array {
    func toDict<K,V>(map: (T) -> (key: K, value: V)?) -> [K: V] {
        var dict = [K: V]()
        for element in self {
            if let (key, value) = map(element) {
                dict[key] = value
            }
        }
        return dict
    }
}

Calling the above would look like this.

let x = ["a","b","c"]
let y = x.toDict { (element) -> (key: String, value: String)? in
    return (key: element, value: "this is the value for \(element)")
}
like image 146
hennes Avatar answered Nov 14 '22 22:11

hennes