我目前有2个列表List('a','b','a')和List(45,65,12),第二个列表中有更多元素和元素通过键来链接到第一个列表中的元素价值关系。我希望通过添加相应的值来组合具有相同键的元素,并创建一个看起来像Map('a' - > 57,'b' - > 65)的地图,如57 = 45 + 12。
我目前已将其实施为
val keys = List('a','b','a')
val values = List(45,65,12)
val finalMap:Map(char:Int) =
scala.collection.mutable.Map().withDefaultValue(0)
0 until keys.length map (w => finalMap(keys(w)) += values(w))
我觉得应该有一种更好的方式(功能方式)来创建所需的地图而不是我这样做。我怎样才能改进我的代码并以更实用的方式做同样的事情?
答案 0 :(得分:6)
val m = keys.zip(values).groupBy(_._1).mapValues(l => l.map(_._2).sum)
编辑:为了解释代码是如何工作的,zip
将两个输入序列的相应元素配对,所以
keys.zip(values) = List((a, 45), (b, 65), (a, 12))
现在,您希望将具有相同第一个元素的所有对组合在一起。这可以通过groupBy
:
keys.zip(values).groupBy(_._1) = Map((a, List((a, 45), (a, 12))), (b, List((b, 65))))
groupBy
返回一个地图,其键是被分组的类型,其值是输入序列中具有相同键的元素列表。
此地图的键是keys
中的字符,值是来自keys
和values
的关联对的列表。由于键是您在输出映射中所需的键,因此您只需将值从List[Char, Int]
转换为List[Int]
。
您可以通过对列表中每对的第二个元素的值求和来实现此目的。
您可以使用map
例如
List((a, 45), (a, 12)).map(_._2) = List(45,12)
现在,您可以使用sum
:
List(45, 12).sum = 57
您可以使用mapValues
将此变换应用于地图中的所有值,以获得所需的结果。
答案 1 :(得分:4)
我打算给李的第一个版本+1,但mapValues
是一个视图,而且对我来说看起来就像是一个。只是不要显得小气。
scala> (keys zip values) groupBy (_._1) map { case (k,v) => (k, (v map (_._2)).sum) }
res0: scala.collection.immutable.Map[Char,Int] = Map(b -> 65, a -> 57)
嘿,折叠的答案消失了。你不能在SO上眨眼,动作如此之快。
无论如何,我打算给李的打字速度+1。
编辑:解释mapValues
如何成为观点:
scala> keys.zip(values).groupBy(_._1).mapValues(l => l.map { v =>
| println("OK mapping")
| v._2
| }.sum)
OK mapping
OK mapping
OK mapping
res2: scala.collection.immutable.Map[Char,Int] = Map(b -> 65, a -> 57)
scala> res2('a') // recomputes
OK mapping
OK mapping
res4: Int = 57
有时这就是你想要的,但往往令人惊讶。我认为它有一个puzzler。
答案 2 :(得分:0)
您实际上正处于合理有效的功能解决方案的正确轨道上。如果我们只是切换到一个不可变的集合并在键值zip上使用折叠,我们得到:
( Map[Char,Int]() /: (keys,values).zipped ) ( (m,kv) =>
m + ( kv._1 -> ( m.getOrElse( kv._1, 0 ) + kv._2 ) )
)
或者您可以使用withDefaultValue 0
,如果您希望最终地图具有该默认值。请注意,.zipped
比zip
快,因为它不会创建中间集合。而groupBy
会创建许多其他中间集合。当然,它可能不值得优化,如果它可以做得比这更好,但我想告诉你,你的思路并不是很遥远。