I want to get from this array of strings
let entries = ["x=5", "y=7", "z=10"]
to this
let keyValuePairs = ["x" : "5", "y" : "7", "z" : "10"]
I tried to use map
but the problem seems to be that a key - value pair in a dictionary is not a distinct type, it's just in my mind, but not in the Dictionary type so I couldn't really provide a transform function because there is nothing to transform to. Plus map
return an array so it's a no go.
Any ideas?
Swift 4
As alluded to by fl034, this can be simplified some with Swift 4 where an error checked version looks like:
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
}
}
Even simpler if you don't want the values as Ints:
let foo = entries
.map { $0.components(separatedBy: "=") }
.reduce(into: [String:String]()) { dict, pair in
if pair.count == 2 {
dict[pair[0]] = pair[1]
}
}
Older TL;DR
Minus error checking, it looks pretty much like:
let foo = entries.map({ $0.componentsSeparatedByString("=") })
.reduce([String:Int]()) { acc, comps in
var ret = acc
ret[comps[0]] = Int(comps[1])
return ret
}
Use map to turn the [String]
into a split up [[String]]
and then build the dictionary of [String:Int]
from that using reduce.
Or, by adding an extension to Dictionary
:
extension Dictionary {
init(elements:[(Key, Value)]) {
self.init()
for (key, value) in elements {
updateValue(value, forKey: key)
}
}
}
(Quite a useful extension btw, you can use it for a lot of map/filter operations on Dictionaries, really kind of a shame it doesn't exist by default)
It becomes even simpler:
let dict = Dictionary(elements: entries
.map({ $0.componentsSeparatedByString("=") })
.map({ ($0[0], Int($0[1])!)})
)
Of course, you can also combine the two map calls, but I prefer to break up the individual transforms.
If you want to add some error checking, flatMap
can be used instead of 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
}})
)
Again, if you want, you can obviously merge the map
into the flatMap
or split them for simplicity.
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
}}
)