在Scala中合并两个HashMaps

时间:2014-08-16 21:10:18

标签: scala scala-collections

我需要创建

def MergeWith[Id, X, Y, Z](x: HashMap[Id, X], y: HashMap[Id, Y], f: (X, Y) => Z): HashMap[Id, Z]

我想:

1)迭代x

2)对于y中显示的键,f(x[key], y[key])生成HashMap[Id, Z]

对于我发明的一切,我得到编译错误,我不明白如何战斗。

例如,

def andWith[K, X, Y, Z] (x: HashMap[K, X], y: HashMap[K, Y], f: (X, Y) => Z): HashMap[K, Z] = {
  for {
    (x_name, x_value) <- x
    if y.contains(x_name)
  } yield x_name -> f(x_value, y.get(x_name))
}

产生

Error:(14, 39) type mismatch;
 found   : Option[Y]
 required: Y
 } yield x_name -> f(x_value, y.get(x_name))
                              ^

这是可预测的,但我无法打开选项。

1 个答案:

答案 0 :(得分:7)

以下是在Scala中编写此内容的非常简洁且合理的惯用方法:

def mergeWith[K, X, Y, Z](xs: Map[K, X], ys: Map[K, Y])
  (f: (X, Y) => Z): Map[K, Z] =
    xs.flatMap {
      case (k, x) => ys.get(k).map(k -> f(x, _))
    }

请注意,我使用的是Map而不是HashMap,并且我稍微更改了一些标识符名称。我还将该函数放入其自己的参数列表中,这可以使语法对用户来说更清晰。

这是非常密集的代码,所以我将解压缩一下。对于(k, v)地图中的每个键值对xs,如果ys.get(k)中的密钥存在,我们会使用Option[Y]来获得Some[Y] ys 1}}和None否则。我们使用map上的OptionY内的(K, Z)值(如果存在)更改为ys.get(k).map(k -> f(x, _))对。

整个Option[(K, Z)]部分的值因此是map,它是一对或零对的集合。如果我们在xs上使用Seq[Option[(K, Z)]],我们最终会得到flatMap,但由于我们使用了Map[K, Z],因此可以将其折叠为{ {1}},这就是我们想要的(以及我们指定的方法的返回类型。

我们可以尝试一下:

scala> val mapX = Map('a -> 1, 'b -> 2)
mapX: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 1, 'b -> 2)

scala> val mapY = Map('b -> "foo", 'c -> "bar")
mapY: scala.collection.immutable.Map[Symbol,String] = Map('b -> foo, 'c -> bar)

scala> mergeWith(mapX, mapY) { (x, y) => (x, y) }
res0: Map[Symbol,(Int, String)] = Map('b -> (2,foo))

这就是我们想要的。

相关问题