Scala将Map序列减少为每个键都有最大值的Map

Posted

技术标签:

【中文标题】Scala将Map序列减少为每个键都有最大值的Map【英文标题】:Scala reduce Sequence of Map to a Map with max value for each key 【发布时间】:2020-09-04 18:08:21 【问题描述】:

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

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

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

预期结果:

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

如何减少map的序列?

【问题讨论】:

都是单例地图吗? 它们是不可变的地图 Map("k1",1) 语法无效。你有元组"k1" -> 1吗? 是的,我很抱歉@MarioGalic 您确定您的预期结果是一系列地图吗?也许你只想要一张地图?另外,你的输入是一个单例映射序列很奇怪,也许你更愿意有一个元组序列? 【参考方案1】:

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)

【讨论】:

【参考方案2】:

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

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)

这应该是performant,因为我们是单次遍历列表,而Map 的查找/添加默认情况下实际上是恒定时间。

【讨论】:

【参考方案3】:
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>

【讨论】:

我无法解析值“values.map(_._2).max”上的 .map 这很奇怪。 scastie 代码工作正常。编译器告诉你什么?【参考方案4】:

如果您决定使用映射而不是元组,请使用这个简短(但不一定高效)的版本:

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

否则,你可以使用

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

【讨论】:

以上是关于Scala将Map序列减少为每个键都有最大值的Map的主要内容,如果未能解决你的问题,请参考以下文章

将 Java 映射转换为 Scala 映射

如何编写hadoop map减少scala中的程序

scala 基础六 scala Map和元组的操作

Scala 将多个元组中的 ID 减少为一个 ID

PHP数组重复:每个字符串键都有数字键

scala占位符_的用法