Frage

Beginnend mit einer Liste von Objekten, die zwei Parameter fiktiv und Währung, wie kann ich die Nominal pro Währung aggregieren?

Gegeben:

case class Trade(name: String, amount: Int, currency: String)

val trades = List(
  Trade("T150310", 10000000, "GBP"),
  Trade("T150311", 10000000, "JPY"),
  Trade("T150312", 10000000, "USD"),
  Trade("T150313", 100, "JPY"),
  Trade("T150314", 1000, "GBP"),
  Trade("T150315", 10000, "USD")
)

Wie kann ich:

Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)
War es hilfreich?

Lösung

Ich schrieb eine einfache Gruppe-by-Betrieb (eigentlich ein Groupable trait mit einer impliziten Umwandlung von einem Iterable), die Sie gruppieren Sie Ihre Trades durch ihre currency erlauben würde:

trait Groupable[V] extends Iterable[V] {
  def groupBy(f: V => K): MultiMap[K, V] = {
    val m = new mutable.HashMap[K, Set[V]] with mutable.MultiMap[K, V]
    foreach { v => m add (f(v), v) } //add is defined in MultiMap
    m
  }
}
implicit def it2groupable(it: Iterable[V]): Groupable[V] = new Groupable[V] {
  def elements = it.elements
}

So Groupable ist einfach ein Weg, um eine Schlüssel zu extrahieren von jedem Element in einem Iterable und dann Gruppierung alle diese Elemente, die den gleichen Schlüssel haben. Also, in Ihrem Fall:

//mm is a MultiMap[Currency, Trade]
val mm = trades groupBy { _.currency } 

Sie können nun eine ganz einfache mapElements tun (mm ist ein Map) und eine foldLeft (oder /: - lohnt sich das Verständnis der foldLeft Betreiber, da es extrem kurze Aggregationen über Sammlungen ermöglicht), um die Summe zu erhalten:

val sums: Map[Currency, Int] = mm mapElements { ts => 
    (0 /: ts) { (sum,t) => sum + t.notional } 
}

Entschuldigt, wenn ich habe ein paar Fehler in der letzten Zeile gemacht. ts sind die Werte von mm, die (natürlich) Iterable[Trade] sind.

Andere Tipps

Wenn Sie Stamm verwenden die Maschinen sind schon da. groupBy ist definiert auf Travers und Summe kann auf die Liste direkt angewendet werden, Sie müssen nicht eine Falte schreiben.

scala> trades groupBy (_.currency) map { case (k,v) => k -> (v map (_.amount) sum) }
res1: Iterable[(String, Int)] = List((GBP,10001000), (JPY,10000100), (USD,10010000))

Ab Scala 2.13 sind die meisten Sammlungen mit der groupMapReduce Verfahren die (wie der Name schon sagt) ein Äquivalent (effizienteren) eines groupBy gefolgt von mapValues und einen Schritt verringern:

trades.groupMapReduce(_.currency)(_.amount)(_ + _)
// immutable.Map[String,Int] = Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)

Dieses:

  • groups Elemente auf der Grundlage ihrer Währung (Gruppe Teil von Gruppe MapReduce)

  • maps gruppiert Werte in ihrer Höhe (Karte Teil der Gruppe Karte Verkleinern)

  • reduces Wert (_ + _) von ihnen Summieren (reduzieren Teil groupMap reduzieren ).

Dies ist eine äquivalente Version durchgeführt in einem durch~~POS=TRUNC durch die Liste der:

trades.groupBy(_.currency).mapValues(_.map(_.amount).reduce(_+_))
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top