Scala 合并两个具有不同值类型的映射
Scala merge two maps with different value type
给定两张地图
val m1 = Map("a" -> "a")
val m2 = Map("a" -> 1)
作为合并这两者的结果,我想得到
Map("a" -> ("a",1))
我为此尝试了 cats semigroupal,它几乎完成了工作,但我必须自己为 semigroupal 创建实例,就像这样
Semigroupal.catsSemigroupalForMap[String]
然后用product
方法合并它们。我需要做什么才能不手动创建它?例如。合并两个选项很简单
val noneInt: Option[Int] = None
val some3: Option[Int] = Some(3)
Semigroupal[Option].product(noneInt, some)
我想实现一个类似的代码来合并两个地图,但是在定义时
Semigroupal[Map]
编译器找不到它的任何隐式。
这似乎是 Align
的一个很好的用例
import cats.data.Ior
import cats.syntax.all._
def combineMaps[K, A, B](m1: Map[K, A], m2: Map[K, B]): Map[K, (A, B)] =
m1.align(m2).collect {
case (key, Ior.Both(a, b)) => key -> (a, b)
}
可以这样使用:
val m1 = Map("a" -> "foo", "b" -> "bar")
val m2 = Map("a" -> 1, "c" -> 3)
val result = combineMaps(m1, m2)
// result: Map[String, (String, Int)] = Map("a" -> ("foo", 1))
可以看到代码运行 here.
tupled
似乎可以解决问题:
@ val mapA = Map(1 -> 2)
mapA: Map[Int, Int] = Map(1 -> 2)
@ val mapB = Map(1 -> "b")
mapB: Map[Int, String] = Map(1 -> "b")
@ (mapA, mapB).tupled
res4: Map[Int, (Int, String)] = Map(1 -> (2, "b"))
它一般编译:
@ def mergeMaps[A, B, C](m1: Map[A, B], m2: Map[A, C]): Map[A, (B, C)] = (m1, m2).tupled
defined function mergeMaps
@ mergeMaps(mapA, mapB)
res6: Map[Int, (Int, String)] = Map(1 -> (2, "b"))
它会破坏两个映射中都不存在的键:
@ val m1 = Map("a" -> "foo", "b" -> "bar")
m1: Map[String, String] = Map("a" -> "foo", "b" -> "bar")
@ val m2 = Map("a" -> 1, "c" -> 3)
m2: Map[String, Int] = Map("a" -> 1, "c" -> 3)
@ mergeMaps(m1, m2)
res9: Map[String, (String, Int)] = Map("a" -> ("foo", 1))
Vanilla Scala 也可以为您服务。要合并多个地图,您需要:
- 将输入映射转换为元组序列并追加所有内容
- groupBy 再次进入地图。这将累积来自同一键的值。
- 从输出地图中选择你想要的任何东西
def merge(first: Map[String, Int], rest: Map[String, Int]*): Map[String, Seq[Int]] =
rest
.iterator
.foldLeft(first.toSeq) { _ ++ _ }
.groupBy(_._1)
.mapValues(pairs => pairs.map(_._2))
val m1 = Map("b" -> 2)
val m2 = Map("a" -> 1)
val m3 = Map("a" -> 1)
merge(m1, m2, m3)
merge(m1, m2)
merge(m1)
给定两张地图
val m1 = Map("a" -> "a")
val m2 = Map("a" -> 1)
作为合并这两者的结果,我想得到
Map("a" -> ("a",1))
我为此尝试了 cats semigroupal,它几乎完成了工作,但我必须自己为 semigroupal 创建实例,就像这样
Semigroupal.catsSemigroupalForMap[String]
然后用product
方法合并它们。我需要做什么才能不手动创建它?例如。合并两个选项很简单
val noneInt: Option[Int] = None
val some3: Option[Int] = Some(3)
Semigroupal[Option].product(noneInt, some)
我想实现一个类似的代码来合并两个地图,但是在定义时
Semigroupal[Map]
编译器找不到它的任何隐式。
这似乎是 Align
import cats.data.Ior
import cats.syntax.all._
def combineMaps[K, A, B](m1: Map[K, A], m2: Map[K, B]): Map[K, (A, B)] =
m1.align(m2).collect {
case (key, Ior.Both(a, b)) => key -> (a, b)
}
可以这样使用:
val m1 = Map("a" -> "foo", "b" -> "bar")
val m2 = Map("a" -> 1, "c" -> 3)
val result = combineMaps(m1, m2)
// result: Map[String, (String, Int)] = Map("a" -> ("foo", 1))
可以看到代码运行 here.
tupled
似乎可以解决问题:
@ val mapA = Map(1 -> 2)
mapA: Map[Int, Int] = Map(1 -> 2)
@ val mapB = Map(1 -> "b")
mapB: Map[Int, String] = Map(1 -> "b")
@ (mapA, mapB).tupled
res4: Map[Int, (Int, String)] = Map(1 -> (2, "b"))
它一般编译:
@ def mergeMaps[A, B, C](m1: Map[A, B], m2: Map[A, C]): Map[A, (B, C)] = (m1, m2).tupled
defined function mergeMaps
@ mergeMaps(mapA, mapB)
res6: Map[Int, (Int, String)] = Map(1 -> (2, "b"))
它会破坏两个映射中都不存在的键:
@ val m1 = Map("a" -> "foo", "b" -> "bar")
m1: Map[String, String] = Map("a" -> "foo", "b" -> "bar")
@ val m2 = Map("a" -> 1, "c" -> 3)
m2: Map[String, Int] = Map("a" -> 1, "c" -> 3)
@ mergeMaps(m1, m2)
res9: Map[String, (String, Int)] = Map("a" -> ("foo", 1))
Vanilla Scala 也可以为您服务。要合并多个地图,您需要:
- 将输入映射转换为元组序列并追加所有内容
- groupBy 再次进入地图。这将累积来自同一键的值。
- 从输出地图中选择你想要的任何东西
def merge(first: Map[String, Int], rest: Map[String, Int]*): Map[String, Seq[Int]] =
rest
.iterator
.foldLeft(first.toSeq) { _ ++ _ }
.groupBy(_._1)
.mapValues(pairs => pairs.map(_._2))
val m1 = Map("b" -> 2)
val m2 = Map("a" -> 1)
val m3 = Map("a" -> 1)
merge(m1, m2, m3)
merge(m1, m2)
merge(m1)