Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Friday, October 28, 2016

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

Popular Posts

Powered by Blogger.