Scala 将映射序列缩减为具有每个键的最大值的映射



我有一个这样的地图序列:

Seq(
Map("k1" -> 1),
Map("k1" -> 2),
Map("k2" -> 3),
Map("k2" -> 4)
)

我想减少到一个值等于每个(key,value)最大值的单个地图

预期成果:

Seq(
Map("k1" -> 2),
Map("k2" -> 4)
)

如何减少地图的顺序?

2.13上你可以这样做:

def mergeMapsWithMax[K, V : Ordering](data: IterableOnce[Map[K, V]]): Map[K, V] =
data
.iterator
.flatten
.toList
.groupMapReduce(_._1)(_._2)(Ordering[V].max)

你可以像这样使用:

val data = Seq(
Map("k1" -> 1),
Map("k1" -> 2),
Map("k2" -> 3),
Map("k2" -> 4)
)
// data: Seq[scala.collection.immutable.Map[String,Int]] = List(Map(k1 -> 1), Map(k1 -> 2), Map(k2 -> 3), Map(k2 -> 4))

mergeMapsWithMax(data)
// res: Map[String,Int] = Map(k1 -> 2, k2 -> 4)

假设您重新考虑使用元组列表而不是映射序列

val tuples = List(
("k1", 1),
("k1", 2),
("k2", 3),
("k2", 4)
)

尝试这样foldLeft

tuples.foldLeft(Map.empty[String, Int]) { case (acc, t @ (key, value)) =>
acc.get(key) match {
case Some(oldValue) => if (oldValue >= value) acc else acc + t
case None => acc + t
}
}
// val res0: Map[String,Int] = Map(k1 -> 2, k2 -> 4)

或使用updatedWith

tuples.foldLeft(Map.empty[String, Int]) { case (acc, t @ (key, value)) =>
acc.updatedWith(key) {
case Some(oldValue) => Some(math.max(oldValue, value))
case None => Some(value)
}
}
// val res1: Map[String,Int] = Map(k1 -> 2, k2 -> 4)

这应该是相当高性能的,因为我们是单次遍历列表,并且Map的查找/添加默认情况下具有有效的恒定时间。

Seq(Map("k1" -> 1), Map("k1" -> 2), Map("k2" -> 3), Map("k2" -> 4))
.reduce { (m1, m2) =>
(m1.toSeq ++ m2.toSeq).groupBy(_._1).map {
case (k, values) => k -> values.map(_._2).max
}
}

生产

Map(k2 -> 4, k1 -> 2)

<script src="https://scastie.scala-lang.org/3aqyPILyRAS1tUagYcpq7w.js"></script>

如果您决定使用地图而不是元组,请使用这个简短(但不一定有效(的版本:

mapSeq.flatMap(_.toList).groupBy(_._1).map(_._2.max)

否则,您可以使用

tupleSeq.groupBy(_._1).map(_._2.max)

最新更新