Is there a declarative way to transform Array to Dictionary?
Is there a declarative way to transform Array to Dictionary?
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?
Answer by Paul Griffiths for Is there a declarative way to transform Array to Dictionary?
One way to do it is in two stages with map
and reduce
with a tuple as an intermediate value, for instance:
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]).") }
outputs:
Value for key 'y' is Optional("7"). Value for key 'x' is Optional("5"). Value for key 'z' is Optional("10").
or with a single reduce
with the same output:
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]).") }
Answer by David Berry for Is there a declarative way to transform Array to Dictionary?
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 }} )
Answer by Daniel T. for Is there a declarative way to transform Array to Dictionary?
I like @paulgriffiths answer but if you have an extreme aversion to runtime errors, you can take it a step further to ensure every string in the initial array actually has both required elements...
The important difference in this code compared to the others is that I check to ensure that there actually is an "=" in the string with elements on both sides. The flatMap
effectively filters out any that fail.
extension Dictionary { func withUpdate(key: Key, value: Value) -> Dictionary { 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) }
Answer by Ian Bytchek for Is there a declarative way to transform Array to Dictionary?
And for everyone else who enjoys overloading and one-liners.
public func +(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"]
Answer by user3255356 for Is there a declarative way to transform Array to Dictionary?
Good answers already. Here is an additional way with a collection type extension. You can convert any collection type to either a dictionary, array or set.
extension CollectionType { func map2Dict(@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(@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(@noescape map: ((Self.Generator.Element) -> (T)?)) -> Set { return Set(map2Array(map)) } }
Here's the example usage.
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)")
Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 72
0 comments:
Post a Comment