在Swift 2中使用字典的map函数,返回一个字典

11

我在学习如何使用Swift 2中的map方法:

我正在读取一个字典(从plist文件中读取),因此我有一个[String: AnyObject]

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

我的目标是将一个由字符串组成的字典转化为一组日志记录器实例的字典。这将是[String: XCGLogger]

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

然而,这返回的是一个[[String:XCGLogger]](在我看来就像是一个字典数组)。问题是如何返回一个扁平化的字典。当我尝试使用flatMap时,我开始陷入循环错误中,涉及到闭包或无法在类型(Key,Value)-> NSDictionary上调用flatMap。

3个回答

8
原因在于map只能返回数组,而不能返回字典。要获得字典,有几种策略可用,例如:
var loggers : [String: XCGLogger] = [:]
dictionary.map{(n, l) in loggers[n] = newLogger(l.0, withLevel: level(l.1))}

也许可以这样理解:
var loggers : [String: XCGLogger] = [:]
for (n, l) in dictionary {
  loggers[n] = newLogger(l.0, withLevel: level(l.1))
}

记录器

1
我对此的问题在于它是一种非功能性方法,作为首要的Scala程序员,这让我感到不安。难道没有办法从map返回一个Dictionary吗?我讨厌依赖副作用来实现我的目标的想法...!(我甚至会选择返回一个数组,然后将其转换为字典...) - Zaphod
从手册中可以得知: "Map返回一个包含将transform映射到self上的结果的数组". 因此它返回一个数组,而不能返回一个字典。我理解你担心副作用,但是请注意,Swift有一种非常好的方法来限制副作用,即使用varletstructclasses,并且基本框架需要副作用,原本是用C和Objective-C编写的。因此,如果您想继续在Swift和iOS上进行编程,恐怕您必须熟悉非函数式编程技术。 - Renzo
明白了 - 不过有点遗憾,我本来希望Swift 2会更加“函数化”。不过它确实取得了很大进步...每一次迭代都越来越接近目标。我并不指望Swift会变成纯粹的函数式语言,但我确实喜欢看到这些特性的发展。 - Zaphod
是的,我认为你说得对,Swift2越来越实用。太好了! - Renzo

5
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))) }

或者

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))) }

或者
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))) }

1

@Renzo的回答已经提供了一种更加简洁的方法,可以使用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
}

如果Swift可以将kv解构为(key, value),那么它将更方便和清晰。希望在未来的Swift版本中,我们能够实现这一点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接