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
.
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
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))) }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With