2017-01-06 27 views
1

新功能以更「功能」的風格進行編程。通常我會編寫一系列嵌套的foreach循環和總計+=在Scala中使用嵌套地圖滾動數據

我有一個數據結構,看起來像:

Map(
    "team1" -> 
    Map(
     "2015" -> Map("wins" -> 30, "losses" -> 5), 
     "2016" -> Map("wins" -> 3, "losses" -> 7) 
    ), 
    "team2" -> 
    Map(
     "2015" -> Map("wins" -> 22, "losses" -> 1), 
     "2016" -> Map("wins" -> 17, "losses" -> 4) 
    ) 
) 

我要的是簡單地扔掉年份信息,並通過團隊增加了勝/負在一起的數據結構。

Map(
    "team1" -> Map("wins" -> 33, "losses" -> 12), 
    "team2" -> Map("wins" -> 39, "losses" -> 5) 
) 

我一直在尋找groupBy但如果我沒有這個嵌套結構,它似乎僅是有用的。

任何想法?或者更傳統的命令/習俗方法在這裏是有利的。

回答

2
myMap.map(i => i._1 -> i._2.values.flatMap(_.toList).groupBy(_._1).map(i => i._1 -> i._2.map(_._2).sum)) 
  1. 得到所有值
  2. flatMap來者皆
  3. 得到的所有分組值列表
  4. groupBy和總結
+0

謝謝!我最終將您的解決方案修改爲'mapValues(_。values.flatten.groupBy(_._ 1).mapValues(_。map(_._ 2).sum))',但是這很有效。 – diplosaurus

1

使用cats你可以這樣做:

import cats.implicits._ 
// or 
// import cats.instances.map._ 
// import cats.instances.int._ 
// import cats.syntax.foldable._ 

teams.mapValues(_.combineAll) 
// Map(
// team1 -> Map(wins -> 33, losses -> 12), 
// team2 -> Map(wins -> 39, losses -> 5) 
//) 

combineAll結合每年使用Monoid[Map[String, Int]]實例的勝/負圖(也由貓庫提供,見Monoid documentation),其中求和Int S代表每一個關鍵。

+0

看起來不錯,只是如何選擇'0/+'在'1/*'上代替'Int'?後者也是一個幺半羣。 –

+0

@VictorMoroz Cats(和Scalaz)中默認的'Monoid [Int]'使用加法 –

0
scala> val sourceMap = Map(
    |  "team1" -> 
    |  Map(
    |   "2015" -> Map("wins" -> 30, "losses" -> 5), 
    |   "2016" -> Map("wins" -> 3, "losses" -> 7) 
    |  ), 
    |  "team2" -> 
    |  Map(
    |   "2015" -> Map("wins" -> 22, "losses" -> 1), 
    |   "2016" -> Map("wins" -> 17, "losses" -> 4) 
    |  ) 
    | ) 
sourceMap: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,Int]]] = Map(team1 -> Map(2015 -> Map(wins -> 30, losses -> 5), 2016 -> Map(wins -> 3, losses -> 7)), team2 -> Map(2015 -> Map(wins -> 22, losses -> 1), 2016 -> Map(wins -> 17, losses -> 4))) 

scala> sourceMap.map { case (team, innerMap) => 
    |  val outcomeGroups = innerMap.values.flatten.groupBy(_._1) 
    |  team -> outcomeGroups.map { case (outcome, xs) => 
    |  val scores = xs.map(_._2).sum 
    |  outcome -> scores 
    |  } 
    | } 
res0: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,Int]] = Map(team1 -> Map(losses -> 12, wins -> 33), team2 -> Map(losses -> 5, wins -> 39)) 
1
.mapValues { _.toSeq 
       .flatMap(_._2.toSeq) 
       .groupBy(_._1) 
       .mapValues(_.foldLeft(0)(_ + _._2)) } 
2

定義一個定製的方法,通過按鍵來添加兩個地圖:

def addMap(x: Map[String, Int], y: Map[String, Int]) = 
    x ++ y.map{ case (k, v) => (k, v + x.getOrElse(k, 0))} 


m.mapValues(_.values.reduce(addMap(_, _))) 
// res16: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,Int]] = 
// Map(team1 -> Map(wins -> 33, losses -> 12), team2 -> Map(wins -> 39, losses -> 5))