Scala:从元组列表构建一个Map,但如果存在矛盾的条目则会失败

zig*_*tar 6 collections scala

我认为这可能是一种常见的操作.所以也许它在API内部,但我找不到它.如果没有,我也对有效的功能/简单解决方案感兴趣.

给定一系列元组,("a" -> 1, "b" ->2, "c" -> 3)我想把它变成一个地图.这很容易使用TraversableOnce.toMap.但是,如果结果地图"将包含矛盾",即分配给同一个键的不同值,我想要失败.就像在序列中一样("a" -> 1, "a" -> 2).但是应该允许重复.

目前我有这个(非常迫切的)代码:

def buildMap[A,B](in: TraversableOnce[(A,B)]): Option[Map[A,B]] = {
  val map = new HashMap[A,B]
  val it = in.toIterator
  var fail = false
  while(it.hasNext){
    val next = it.next()
    val old = map.put(next._1, next._2)
    fail = old.isDefined && old.get != next._2
  }

  if(fail) None else Some(map.toMap)
}
Run Code Online (Sandbox Code Playgroud)

边问题

决赛toMap真的有必要吗?省略它时会出现类型错误,但我认为它应该有效.实现toMap构建一个我想避免的新地图.

Mor*_*itz 6

一如既往,在使用Seq[A]最佳解决方案时,性能取决于具体的收集类型.一般但不是非常有效的解决方案是折叠Option[Map[A,B]]:

def optMap[A,B](in: Iterable[(A,B)]): Option[Map[A,B]] = 
  in.iterator.foldLeft(Option(Map[A,B]())) {
    case (Some(m),e @ (k,v)) if m.getOrElse(k, v) == v => Some(m + e)
    case _ => None
  }
Run Code Online (Sandbox Code Playgroud)

如果您限制自己使用List[A,B]s,则优化版本将是:

@tailrec
def rmap[A,B](in: List[(A,B)], out: Map[A,B] = Map[A,B]()): Option[Map[A,B]] = in match {
  case (e @ (k,v)) :: tail if out.getOrElse(k,v) == v =>
    rmap(tail, out + e)
  case Nil =>
    Some(out)
  case _ => None
}
Run Code Online (Sandbox Code Playgroud)

另外,使用可变映射的不太惯用的版本可以像这样实现:

def mmap[A,B](in: Iterable[(A,B)]): Option[Map[A,B]] = {
  val dest = collection.mutable.Map[A,B]()

  for (e @ (k,v) <- in) {
    if (dest.getOrElse(k, v) != v) return None
    dest += e
  }

  Some(dest.toMap)
}
Run Code Online (Sandbox Code Playgroud)