2

私はリストを持っています

    val l : List[Map[String,Any]] = List(Map("a" -> 1, "b" -> 2.8), Map("a" -> 3, "c" -> 4), Map("c" -> 5, "d" -> "abc"))

次のコードを使用して、キー "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)))

出力は (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)

例外は、「+」が AnyVal では機能しないことを伝えようとしていたと思います。これを機能させて、必要な結果を得るにはどうすればよいですか? ありがとう

4

6 に答える 6

6
m.foldLeft(0)(_+_._2)  

これは非常に明確でシンプルなソリューションです。参照: http://ktuman.blogspot.com/2009/10/how-to-simply-sum-values-in-map-in.html

于 2016-06-15T09:42:12.003 に答える
5

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

collectの代わりにincrop のアイデアを使用して更新されましmatchた。

于 2012-04-04T09:27:28.743 に答える
3

まず、パターン マッチングのポイントを完全に見逃しています。

{case i: Int => i
 case d: Double => d
 case _ => 0} 

内のすべての関数を適切に置き換えるものmapValuesです。しかし、これは問題ではありません。あなたの文章は複雑ですが、同じことをします。

あなたの関数がmapValues戻りますDouble(いくつかのブランチが戻りInt、他のブランチが戻りDouble、この場合、Intに昇格されるためDoubleです。そうでない場合は、戻りAnyValます)。だからあなたは得るList[Map[String, Double]]。この時点で、あなたは Ints を失いました。

するとm.get("a")、これが返ってきますOption[Double]Option[A] has method getOrElse(default: A) : A(実際には、default: => X) しかし、ここでは違いはありません)。

getOrElse(0.0)の代わりに呼び出すとgetOrElse(0)、Double が得られます。フォールドが (Int、Double、Double) で始まり、(Double、Double、Double) を返すため、コードはまだ失敗します。フォールドを (0.0, 0.0, 0.0) で開始すると機能しますが、Int が失われ、(4.0, 2.8, 9.0) になります。

さて、エラーメッセージについて。Double (getOrElse) を期待するメソッドに Int を渡すと、通常、Int は Double に変換され、 で呼び出したかのようになりますgetOrElse(0.0)。それが共変であることを除いてOption(宣言されていtrait Option[+A]ます)。Xが の先祖である場合AOption[A]Option[X]です。したがって、Option[Double]Option[AnyVal]Option[Any]です。getOrElse(0)オプションが と見なされる場合、呼び出しは機能Option[AnyVal]し、結果はAnyVal(でも機能しAnyますが、AnyValより正確であり、これがコンパイラーによって選択されます)。0式はそのままコンパイルされるため、 toにプロモートする必要はありません0.0。したがってm.get("a").getOrElse(0)、 は 型AnyValであり、 に追加することはできませんt._1. これがエラーメッセージの内容です。

"a" が Int に関連付けられ、"b" が double に関連付けられていることを知っていますが、この知識をコンパイラに渡しません。

于 2012-04-04T09:55:13.257 に答える
0

気の利いたワンライナー:

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)
于 2012-04-04T16:11:06.070 に答える
0

一般に、キーがわからないが値を合計したい場合は、次のことができます

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)

アップデート

たとえば、必要なタイプでマップをフィルタリングできます

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))

次に値を合計します(リンクされた質問を参照)

scala> intMap reduce { _ |+| _ }
res4: scala.collection.immutable.Map[String,Int] = Map(a -> 4, c -> 9)
于 2012-04-04T09:50:55.187 に答える