Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using map on a Dictionary in Swift 2, to return a Dictionary

I'm having trouble figuring out Swift 2's particular style of map:

I'm reading in a dictionary (from a plist file), so I've got an [String: AnyObject]:

let dictionary = NSDictionary(contentsOfFile: path) as? [String: AnyObject] 

My goal is to transform from a dictionary of Strings into a dictionary of logger instances. This would be [String: XCGLogger]:

let loggers = dictionary
    .map { (n, l) in [ n: newLogger(l.0, withLevel: level(l.1)) ] }

However, this is returning an [[String: XCGLogger]] (which looks like an array of dictionaries to me). The question is how do I return a flattened dictionary. When I try to use flatMap I start running in circles around errors about closures or not being able to call flatMap on a type (Key, Value) -> NSDictionary.

like image 798
Zaphod Avatar asked Aug 23 '15 16:08

Zaphod


3 Answers

The reason is that map can return only arrays, and not dictionaries. To obtain a dictionary you have several strategies, for instance:

var loggers : [String: XCGLogger] = [:]
dictionary.map{(n, l) in loggers[n] = newLogger(l.0, withLevel: level(l.1))}

or perhaps:

var loggers : [String: XCGLogger] = [:]
for (n, l) in dictionary {
  loggers[n] = newLogger(l.0, withLevel: level(l.1))
}

loggers

like image 98
Renzo Avatar answered Nov 08 '22 19:11

Renzo


extension SequenceType {
  func toDict<K : Hashable, V>
    (@noescape convert: Generator.Element -> (K, V)) -> [K:V] {
    var result: [K:V] = [:]
    for x in self {
      let (key, val) = convert(x)
      result[key] = val
    }
    return result
  }
}

dictionary.toDict { (n, l) in (n, newLogger(l.0, withLevel: level(l.1))) }

Or

extension Dictionary {
  public init<
    S : SequenceType where
    S.Generator.Element == (Key, Value)
    >(_ seq: S) {
    self.init()
    for (k, v) in seq { self[k] = v }
  }
}

extension SequenceType {
  func toDict<K : Hashable, V>
    (@noescape convert: Generator.Element -> (K, V)) -> [K:V] {
    return Dictionary(lazy(self).map(convert))
  }
}

dictionary.toDict { (n, l) in (n, newLogger(l.0, withLevel: level(l.1))) }

Or

extension Dictionary {
  func map<K : Hashable, V>(@noescape transform: (Key, Value) -> (K, V)) -> [K:V] {
    var result: [K:V] = [:]
    for x in self {
      let (key, val) = transform(x)
      result[key] = val
    }
    return result
  }
}

dictionary
  .map { (n, l) in (n, newLogger(l.0, withLevel: level(l.1))) }
like image 32
oisdk Avatar answered Nov 08 '22 19:11

oisdk


Slightly more concise way from @Renzo's answer would be using reduce:

let loggers: [String: XCGLogger] = dictionary.reduce([:]){(var loggers, kv) in
loggers[kv.0] = newLogger(kv.1.0, withLevel: level(kv.1.1))
return loggers
}

It would be more convenient and clearer if Swift could destruct kv into (key, value). Hopefully future version of Swift, we will be able to do this.

like image 1
hardboiledbaby Avatar answered Nov 08 '22 21:11

hardboiledbaby