Domanda

Partendo da un elenco di oggetti contenente due parametri nozionale e valuta, come posso aggregare il nozionale totale per valuta?

Data:

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

Come posso ottenere:

Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)
È stato utile?

Soluzione

Ho scritto una semplice operazione di raggruppamento (in realtà un raggruppabile tratto con una conversione implicita da un Iterable ) che ti avrebbe permesso di raggruppa le tue operazioni in base alla loro valuta :

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
}

Quindi Groupable sta semplicemente fornendo un modo per estrarre una chiave da ogni elemento in un Iterable e quindi raggruppando tutti quegli elementi che hanno il stessa chiave. Quindi, nel tuo caso:

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

Ora puoi fare un mapElements abbastanza semplice ( mm è un Map ) e un foldLeft (o /: - vale la pena comprendere l'operatore foldLeft in quanto consente aggregazioni estremamente concise sulle raccolte) per ottenere la somma:

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

Mi scuso se ho commesso degli errori in quest'ultima riga. ts sono i valori di mm , che sono (ovviamente) Iterable [Trade] .

Altri suggerimenti

Se usi il bagagliaio il macchinario è già lì. groupBy è definito su Traversable e la somma può essere applicata direttamente all'elenco, non è necessario scrivere una piega.

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

A partire da Scala 2.13 , la maggior parte delle raccolte sono fornite con groupMapReduce che è (come suggerisce il nome) un equivalente (più efficiente) di un groupBy seguito da mapValues ?? e un ridurre il passo:

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

Questa:

  • group elementi in base alla loro valuta (parte del gruppo di groupMapReduce)

  • mappa valori raggruppati per il loro importo (mappa parte del gruppo Mappa Riduci)

  • riduci i valori ( _ + _ ) sommandoli (riduci parte del gruppo Mappa Riduci ).

Questa è una versione equivalente eseguito in un passaggio attraverso l'elenco di:

trades.groupBy(_.currency).mapValues(_.map(_.amount).reduce(_+_))
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top