Scala 如何在列表中与 Map[String, Double] 类型的映射相交:List[Map[String, Double]]
Scala How to Intersect Maps of types Map[String, Double] inside a List of: List[Map[String, Double]]
所以我有一个地图列表:List[Map[String, Double]]。
一个例子是:
List(Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9),
Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8),
Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4),
Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7))
我想要做的是将所有地图的交点放在一起,这样它看起来像:
For example, for A: (1.1 + 0.8 + 3.4 + 3.7)/4 = 2.25
for D: (0.9 + 2.8 + 2.9 + 1.8)/4 = 2.1
List(Map("A" -> 2.25,"D" -> 2.1))
有没有办法仅使用内置函数来获取上面地图的相交列表?这些值是四个映射中所有键的平均值。
首先尝试使用 reduce
只保留重复键并将所有值相加,然后使用 mapValues
求平均值:
val maps = List(...)
val intersected = maps
.reduce { (m1, m2) =>
m1.keySet.intersect(m2.keySet).map(key => (key, m1(key) + m2(key))).toMap
}
.view
.mapValues(_ / maps.size)
.toMap
类似
必须注意输入不为空。
val lm : List[Map[String,Double]] =
List(Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9)
,Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8)
,Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4)
,Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7))
val len = lm.length
val res = if (len > 0)
lm.map(_.keySet)
.reduce(_ intersect _)
.map(k => (k, lm.map(_(k)).sum/len))
.toMap
else Map.empty[String,Double]
//res: Map[String,Double] = Map(A -> 2.25, D -> 2.1)
假设我们有:
val list = List(Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9),
Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8),
Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4),
Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7))
您的另一个选择是(仅限 Scala 2.13):
val stringToDoubles =
list.flatten
.groupMap(_._1)(_._2)
.filter(_._2.size == list.size)
.map(keyAndValues => (keyAndValues._1, keyAndValues._2.sum / list.size))
代码 运行 可在 scastie 找到。
在 Scala 2.12 及以下版本中它将是:
val stringToDoubles =
list.flatten
.groupBy(_._1)
.filter(_._2.size == list.size)
.map(keyAndValues => (keyAndValues._1, keyAndValues._2.map(_._2).sum / list.size))
代码 运行 可在 scastie 找到。
如果您愿意使用外部库,使用 cats:
会变得非常简单
import cats.data.NonEmptyList
import cats.syntax.all._
val data = NonEmptyList.of(
Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9),
Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8),
Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4),
Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7)
)
val result =
data
.nonEmptySequence
.fmap { group =>
val (sum, count) = group.foldMap(_ -> 1)
sum / count
}
// result: Map[String, Double] = HashMap(A -> 2.25, D -> 2.1)
可以看到代码运行 here.
所以我有一个地图列表:List[Map[String, Double]]。 一个例子是:
List(Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9),
Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8),
Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4),
Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7))
我想要做的是将所有地图的交点放在一起,这样它看起来像:
For example, for A: (1.1 + 0.8 + 3.4 + 3.7)/4 = 2.25
for D: (0.9 + 2.8 + 2.9 + 1.8)/4 = 2.1
List(Map("A" -> 2.25,"D" -> 2.1))
有没有办法仅使用内置函数来获取上面地图的相交列表?这些值是四个映射中所有键的平均值。
首先尝试使用 reduce
只保留重复键并将所有值相加,然后使用 mapValues
求平均值:
val maps = List(...)
val intersected = maps
.reduce { (m1, m2) =>
m1.keySet.intersect(m2.keySet).map(key => (key, m1(key) + m2(key))).toMap
}
.view
.mapValues(_ / maps.size)
.toMap
必须注意输入不为空。
val lm : List[Map[String,Double]] =
List(Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9)
,Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8)
,Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4)
,Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7))
val len = lm.length
val res = if (len > 0)
lm.map(_.keySet)
.reduce(_ intersect _)
.map(k => (k, lm.map(_(k)).sum/len))
.toMap
else Map.empty[String,Double]
//res: Map[String,Double] = Map(A -> 2.25, D -> 2.1)
假设我们有:
val list = List(Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9),
Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8),
Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4),
Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7))
您的另一个选择是(仅限 Scala 2.13):
val stringToDoubles =
list.flatten
.groupMap(_._1)(_._2)
.filter(_._2.size == list.size)
.map(keyAndValues => (keyAndValues._1, keyAndValues._2.sum / list.size))
代码 运行 可在 scastie 找到。
在 Scala 2.12 及以下版本中它将是:
val stringToDoubles =
list.flatten
.groupBy(_._1)
.filter(_._2.size == list.size)
.map(keyAndValues => (keyAndValues._1, keyAndValues._2.map(_._2).sum / list.size))
代码 运行 可在 scastie 找到。
如果您愿意使用外部库,使用 cats:
会变得非常简单import cats.data.NonEmptyList
import cats.syntax.all._
val data = NonEmptyList.of(
Map("A" -> 1.1, "B" -> 2.5, "E" -> 3.5, "C" -> 1.6, "D" -> 0.9),
Map("A" -> 0.8, "C" -> 2.1, "D" -> 2.8),
Map("C" -> 2.2, "D" -> 2.9, "A" -> 3.4),
Map("B" -> 0.4, "D" -> 1.8, "E" -> 0.234, "A" -> 3.7)
)
val result =
data
.nonEmptySequence
.fmap { group =>
val (sum, count) = group.foldMap(_ -> 1)
sum / count
}
// result: Map[String, Double] = HashMap(A -> 2.25, D -> 2.1)
可以看到代码运行 here.