2017-01-06 4 views
1

Neu in der Programmierung in einem "funktionalen" Stil. Normalerweise würde ich eine Reihe von verschachtelten foreach Schleifen und += zu Summen schreiben.Aufrollen von Daten mit verschachtelten Karten in Scala

Ich habe eine Datenstruktur, die wie folgt aussieht:

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

Was ich will, ist eine Datenstruktur, die einfach die Jahresinformationen wegwirft und fügt Gewinne/Verluste zusammen mit Team.

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

Ich habe bei groupBy suchen, aber das scheint nur dann sinnvoll sein, wenn ich diese verschachtelte Struktur nicht haben.

Irgendwelche Ideen? Oder ist der traditionellere Imperativ/Foreach-Ansatz hier günstig?

Antwort

2
myMap.map(i => i._1 -> i._2.values.flatMap(_.toList).groupBy(_._1).map(i => i._1 -> i._2.map(_._2).sum)) 
  1. erhalten alle Werte
  2. flatMap
  3. groupBy durch Schlüssel
  4. die alle gruppierten Werte erhalten aufzulisten und
+0

Dank! Letztendlich habe ich Ihre Lösung in 'mapValues ​​(_. Values.flatten.groupBy (_._ 1) .mapValues ​​(_. Map (_._ 2) .sum))' 'geändert, aber das hat gut funktioniert. – diplosaurus

1

Mit cats summieren Sie tun können:

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 kombiniert die Gewinne/Verluste Karten eines jedes Jahres eine Monoid[Map[String, Int]] Instanz verwenden (auch von der Katzen-Bibliothek zur Verfügung gestellt, siehe Monoid documentation), die summiert die Int s für jeden Schlüssel.

+0

Sieht gut aus, nur wie wählt man '0/+' über '1/*' für 'Int'? Letzteres ist auch ein Monoid. –

+0

@VictorMoroz Der Standardwert 'Monoid [Int]' in Cats (und Scalaz) verwendet Addition –

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

Definieren Sie eine maßgeschneiderte Methode zwei Karten wie Schlüssel zu addieren:

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