在 Scala 中组合两个具有相同键类型但不同值类型的映射

Combine two Maps with same key type, but different value type in scala

我想合并两个键类型相同但值类型不同的映射。

结果应该包含两种值类型的值(可选,因为某些值可能只存在于一个输入映射中)

类型注释是

def combineMaps[T, U, V](map1: Map[T, U], map2: Map[T, V]): Map[T, (Option[U], Option[V])] = {
  ???
}

我知道这可以通过复杂的代码来实现,例如:

(map1.mapValues(Some(_) -> None).toList ++ map2.mapValues(None -> Some(_)).toList) // List[(T, (Option[U], Option[V]))]
      .groupBy(_._1)                                                               // Map[T, List[(T, (Option[U], Option[V]))]]
      .mapValues(_.map(_._2))                                                      // Map[T, List[(Option[U], Option[V])]]
      .mapValues { list => (
        list.collectFirst { case (Some(u), _) => u },
        list.collectFirst { case (_, Some(v)) => v }
      ) }                                                                          // Map[T, (Option[U], Option[V])]

虽然代码可以运行,但它并没有受益于 Map 中的每个键只出现一次这一事实。方法 .toList 删除此类信息。

我正在寻找一些优雅的 scala 方法来做到这一点(可能有 cats/scalaz,但最好没有它们)

是的 cats 有你介绍过吗,这就是 Align typeclass 提供的。

您只需要做:

m1.align(m2)

这将 return 一个 Map[T, Ior[U, V]] 比一对 Options 更好,因为 Ior 保留了两个元素中至少一个必须存在的事实。

keySet 的联合,然后将键映射到 get 的元组应该这样做:

def combineMaps[T, U, V](m1: Map[T, U], m2: Map[T, V]): Map[T, (Option[U], Option[V])] =
  (m1.keySet union m2.keySet).map(k => (k, (m1.get(k), m2.get(k)))).toMap

示例:

combineMaps(Map('a'->1, 'b'->2, 'c'->3), Map('a'->10.0, 'c'->30.0, 'd'->40.0))
// res1: Map[Char,(Option[Int], Option[Double])] = Map(
//   a -> (Some(1),Some(10.0)), b -> (Some(2),None), c -> (Some(3),Some(30.0)), d -> (None,Some(40.0))
// )