Scala sum 映射值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10007920/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scala sum Map values
提问by thlim
I have a List
我有一个清单
val l : List[Map[String,Any]] = List(Map("a" -> 1, "b" -> 2.8), Map("a" -> 3, "c" -> 4), Map("c" -> 5, "d" -> "abc"))
and I used the following code to find the sum for the keys "a" (Int), "b" (Double) and "c" (Int). "d" is included as noise.
我使用以下代码来查找键“a”(Int)、“b”(Double)和“c”(Int)的总和。“d”作为噪声包括在内。
l.map(n => n.mapValues( v => if (v.isInstanceOf[Number]) {v match {
case x:Int => x.asInstanceOf[Int]
case x:Double => x.asInstanceOf[Double]
}} else 0)).foldLeft((0,0.0,0))((t, m) => (
t._1 + m.get("a").getOrElse(0),
t._2 + m.get("b").getOrElse(0.0),
t._3 + m.get("c").getOrElse(0)))
I expect the output would be (4, 2.8, 9) but instead I was trashed with
我预计输出将是 (4, 2.8, 9) 但相反我被丢弃了
<console>:10: error: overloaded method value + with alternatives:
(x: Int)Int <and>
(x: Char)Int <and>
(x: Short)Int <and>
(x: Byte)Int
cannot be applied to (AnyVal)
I think the exception was trying to tell me that '+' doesn't work with AnyVal. How do I get this to work to get my the result that I want? Thanks
我认为异常试图告诉我“+”不适用于 AnyVal。我如何让它发挥作用以获得我想要的结果?谢谢
回答by Jiayu Wang
m.foldLeft(0)(_+_._2)
it's a very clear and simple solution. reference: http://ktuman.blogspot.com/2009/10/how-to-simply-sum-values-in-map-in.html
这是一个非常清晰和简单的解决方案。参考:http: //ktuman.blogspot.com/2009/10/how-to-simply-sum-values-in-map-in.html
回答by senia
You can use foldLeft function:
您可以使用 foldLeft 函数:
scala> val l : List[Map[String,Any]] = List(Map("a" -> 1, "b" -> 2.8), Map("a" -> 3, "c" -> 4), Map("c" -> 5, "d" -> "abc"))
l: List[Map[String,Any]] = List(Map(a -> 1, b -> 2.8), Map(a -> 3, c -> 4), Map(c -> 5, d -> abc))
scala> val (sa, sb, sc) = l.foldLeft((0: Int, 0: Double, 0: Int)){
| case ((a, b, c), m) => (
| a + m.get("a").collect{case i: Int => i}.getOrElse(0),
| b + m.get("b").collect{case i: Double => i}.getOrElse(0.),
| c + m.get("c").collect{case i: Int => i}.getOrElse(0)
| )
| }
sa: Int = 4
sb: Double = 2.8
sc: Int = 9
Updated using incrop's idea of collectinstead of match.
使用 incrop 的collect代替的想法进行更新match。
回答by Didier Dupont
First, you totally miss the point of pattern matching
首先,你完全错过了模式匹配的重点
{case i: Int => i
case d: Double => d
case _ => 0}
is the proper replacement of all your function inside mapValues. Yet this is not the problem, your writing, while complex, does the same thing.
是你里面所有功能的正确替换mapValues。然而这不是问题,你的写作虽然复杂,但做同样的事情。
Your function in mapValuesreturns Double(because some branches return Intand others return Double, and in this case, Intis promoted to Double. If it were not, it would return AnyVal).
So you get a List[Map[String, Double]]. At this point, you have lost the Ints.
您的函数mapValues返回Double(因为一些分支返回Int而其他分支返回Double,在这种情况下,Int被提升为Double。如果不是,它将返回AnyVal)。所以你得到一个List[Map[String, Double]]. 此时,您已经丢失了 Int。
When you do m.get("a"), this returns Option[Double]. Option[A] has method getOrElse(default: A) : A(actually, default: => X) but it makes no difference here).
当你这样做时m.get("a"),它返回Option[Double]。Option[A] has method getOrElse(default: A) : A(实际上,default: => X)但在这里没有区别)。
If you call getOrElse(0.0)instead of getOrElse(0), you get a Double. Your code still fails, because your fold start with (Int, Double, Double), and you would return (Double, Double, Double). If you start your fold with (0.0, 0.0, 0.0), it works, but you have lost your Ints, you get (4.0, 2.8, 9.0)
如果你调用getOrElse(0.0)而不是getOrElse(0),你会得到一个双精度值。您的代码仍然失败,因为您的折叠以 (Int, Double, Double) 开头,并且您将返回 (Double, Double, Double)。如果你用 (0.0, 0.0, 0.0) 开始折叠,它会起作用,但是你失去了你的 Int,你得到 (4.0, 2.8, 9.0)
Now, about the error message. You pass an Int to a method expecting a Double (getOrElse), the Int should normally be converted to Double, and it would be as if you called with getOrElse(0.0). Except that Optionis covariant (declared trait Option[+A]). if Xis an ancestor of A, an Option[A]is also an Option[X]. So an Option[Double]is also Option[AnyVal]and Option[Any]. The call getOrElse(0)works if the option is considered an Option[AnyVal], and the result is AnyVal(would work with Anytoo, but AnyValis more precise and this is the one the compiler chooses). Because the expression compiles as is, there is no need to promote the 0to 0.0. Thus m.get("a").getOrElse(0)is of type AnyVal, which cannot be added to t._1. This is what your error message says.
现在,关于错误消息。您将 Int 传递给需要 Double (getOrElse) 的方法,Int 通常应转换为 Double,就像您使用getOrElse(0.0). 除了Option是协变的(声明trait Option[+A])。如果X是 的祖先A,Option[A]则也是Option[X]。所以 Option[Double]也是Option[AnyVal]和Option[Any]。getOrElse(0)如果选项被视为Option[AnyVal],则调用有效,结果是AnyVal(也可以使用Any,但AnyVal更精确,这是编译器选择的那个)。因为表达式编译如下是,没有必要推广0到0.0。因此m.get("a").getOrElse(0)是类型AnyVal,不能添加到t._1. 这就是您的错误消息所说的。
You have knowledge that "a" is associated with Int, "b" with double, but you don't pass this knowledge to the compiler.
您知道“a”与 Int 相关联,“b”与 double 相关联,但您没有将此知识传递给编译器。
回答by Destin
A nifty one-liner:
一个漂亮的单线:
l.map(_.filterKeys(_ != "d")).flatten groupBy(_._1) map { case (k,v) => v map { case (k2,v2: Number) => v2.doubleValue} sum }
res0: scala.collection.immutable.Iterable[Double] = List(9.0, 4.0, 2.8)
回答by 4e6
In general, if you don't know the keys, but just want to sum values you can do
一般来说,如果你不知道键,但只想总结你可以做的值
val filtered = for {
map <- l
(k, v) <- map
if v.isInstanceOf[Number]
} yield k -> v.asInstanceOf[Number].doubleValue
val summed = filtered.groupBy(_._1) map { case (k, v) => k -> v.map(_._2).sum }
scala> l
res1: List[Map[String,Any]] = List(Map(a -> 1, b -> 2.8), Map(a -> 3, c -> 4), Map(c -> 5, d -> abc))
scala> filtered
res2: List[(String, Double)] = List((a,1.0), (b,2.8), (a,3.0), (c,4.0), (c,5.0))
scala> summed
res3: Map[String,Double] = Map(c -> 9.0, a -> 4.0, b -> 2.8)
Update
更新
You can filter map by type you want, for example
例如,您可以按所需类型过滤地图
scala> val intMap = for (x <- l) yield x collect { case (k, v: Int) => k -> v }
intMap: List[scala.collection.immutable.Map[String,Int]] = List(Map(a -> 1), Map(a -> 3, c -> 4), Map(c -> 5))
and then sum values (see linked question)
然后求和值(见链接问题)
scala> intMap reduce { _ |+| _ }
res4: scala.collection.immutable.Map[String,Int] = Map(a -> 4, c -> 9)
回答by sprague44
Am I missing something or can you not just do:
我是不是遗漏了什么,或者你不能只是做:
map.values.sum
?
?

