简体   繁体   English

Scala 和 Map 值

[英]Scala sum Map values

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).我使用以下代码找到键“a”(Int)、“b”(Double)和“c”(Int)的总和。 "d" is included as noise. “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我希望 output 会是 (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.我认为异常试图告诉我“+”不适用于 AnyVal。 How do I get this to work to get my the result that I want?我如何让它工作以获得我想要的结果? Thanks谢谢

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

You can use foldLeft function:您可以使用 foldLeft function:

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 collect instead of match .使用 incrop 的collect而不是match的想法进行了更新。

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 .是 mapValues 中所有mapValues的正确替换。 Yet this is not the problem, your writing, while complex, does the same thing.但这不是问题,您的写作虽然复杂,但做同样的事情。

Your function in mapValues returns Double (because some branches return Int and others return Double , and in this case, Int is promoted to Double . If it were not, it would return AnyVal ). mapValues 中的mapValues返回Double (因为一些分支返回Int而其他分支返回Double ,在这种情况下, Int被提升为Double 。如果不是,它将返回AnyVal )。 So you get a List[Map[String, Double]] .所以你得到一个List[Map[String, Double]] At this point, you have lost the Ints.此时,您已经失去了 Ints。

When you do m.get("a") , this returns Option[Double] .当您执行m.get("a")时,这将返回Option[Double] Option[A] has method getOrElse(default: A): A (actually, default: => X ) but it makes no difference here). Option[A] has method getOrElse(default: A): A (实际上, default: => X )但它在这里没有区别)。

If you call getOrElse(0.0) instead of getOrElse(0) , you get a Double.如果您调用getOrElse(0.0)而不是getOrElse(0) ,您将得到一个 Double。 Your code still fails, because your fold start with (Int, Double, Double), and you would return (Double, Double, Double).您的代码仍然失败,因为您的折叠以 (Int, Double, Double) 开头,并且您将返回 (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)如果你以 (0.0, 0.0, 0.0) 开始你的折叠,它有效,但你失去了你的 Ints,你得到 (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) .您将 Int 传递给需要 Double (getOrElse) 的方法,Int 通常应转换为 Double,这就好像您使用getOrElse(0.0)调用一样。 Except that Option is covariant (declared trait Option[+A] ).除了Option是协变的(声明trait Option[+A] )。 if X is an ancestor of A , an Option[A] is also an Option[X] .如果XA的祖先,则Option[A]也是Option[X] So an O ption[Double] is also Option[AnyVal] and Option[Any] .所以 O ption[Double]也是Option[AnyVal]Option[Any] The call getOrElse(0) works if the option is considered an Option[AnyVal] , and the result is AnyVal (would work with Any too, but AnyVal is more precise and this is the one the compiler chooses).如果选项被认为是Option[AnyVal] ,则调用getOrElse(0)有效,结果是AnyVal (也可以与Any一起使用,但AnyVal更精确,这是编译器选择的那个)。 Because the expression compiles as is, there is no need to promote the 0 to 0.0 .因为表达式按原样编译,所以不需要将0提升为0.0 Thus m.get("a").getOrElse(0) is of type AnyVal , which cannot be added to t._1 .因此m.get("a").getOrElse(0)AnyVal类型,不能添加到t._1 This is what your error message says.这就是您的错误消息所说的。

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 相关联,但您没有将此知识传递给编译器。

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您可以按所需类型过滤 map,例如

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)

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)

Am I missing something or can you not just do:我是不是遗漏了什么,或者你不能这样做:

map.values.sum

?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM