scala(scanLeft) - 如何(在功能上)获得具有累积值/频率的Map

问题描述 投票:3回答:3

我是Scala的新手,并尝试将具有累积频率的地图作为具有各个地图的地图的值。因此(对于持久化ListMap等顺序的不可变Map)value(i)是原始Map中包含i的所有值的总和,如下所示:

val map = ListMap(0.0 -> 0.1, 0.01 -> 0.2, 0.05 -> 0.3, 0.1 -> 0.4)
val resultMap = ListMap(0.0 -> 0.1, 0.01 -> 0.3, 0.05 -> 0.6, 0.1 -> 1.0)

由于我需要访问以前的键值对,我想出了一个迭代实现,它使用了mutable.LinkedHashMap和一个运行总计,但我发现它很乱,仍然在寻找一种从不可变的地图中获取可变映射的方法(如下面第3行所示):

var cummulativeMap0 = mutable.LinkedHashMap.empty[Double, Double]
var total = 0.0
//line3:
val copyMap = mutable.LinkedHashMap(0.0->0.1, 0.01->0.2, 0.05->0.3, 0.1->0.4) 
map.foreach {
      (kv) => { 
          total = total + copyMap.remove(kv._1).get
          cummulativeMap0.put(kv._1 , total) 
          } 
       }
cummulativeMap0

或者,我可以使用scanLeft,但只能将分割2个列表中的键和值的成本稍后压缩:

val cummValues = map.values.scanLeft(0.0){ (a, b)=>a+b }.tail
val cummmulativeMap2 = (map.keys zip cummValues).toMap

实现这一目标的最惯用/最实用的方法是什么,是否可以直接在地图上使用scanLeft?请帮忙

scala fold
3个回答
3
投票

如果您不想对其进行压缩解压缩,请忽略每个步骤中的上一个键:

import scala.collection.immutable._

val map = ListMap(0.0 -> 0.1, 0.01 -> 0.2, 0.05 -> 0.3, 0.1 -> 0.4)
val res = map.scanLeft((0.0, 0.0)){
  case ((_, acc), (x, y)) => (x, acc + y)
}

println(res)

给你:

ListMap(0.0 -> 0.1, 0.01 -> 0.3, 0.05 -> 0.6, 0.1 -> 1.0)

(达到机器精度,从输出中截断一些零)


4
投票

我相信你可以使用初始值中的虚拟键扫描左侧的地图元素:

val result = map.scanLeft((0.0, 0.0)){case ((_, av),(bk,bv)) => (bk, av + bv)}.tail

0
投票

我想出了zip + map,主要是因为我不知道scanLeft。 :)

scala> val imap = List(0 -> 1, 1 -> 2, 5 -> 3, 1 -> 4)
map: List[(Int, Int)] = List((0,1), (1,2), (5,3), (1,4))

// add neutral element to start with for first element
scala> val m = imap.zip ((0,0) :: imap)
m: List[((Int, Int), (Int, Int))] = List(((0,1),(0,0)), ((1,2),(0,1)), ((5,3),(1,2)), ((1,4),(5,3)))

// same as in scanLeft:
scala> m.map {case ((a, b), (c, d)) => (a, b + d)}
res153: List[(Int, Int)] = List((0,1), (1,3), (5,5), (1,7))

(使用int来简化)。

© www.soinside.com 2019 - 2024. All rights reserved.