Question

À partir d'une liste d'objets contenant deux paramètres notionnel et devise, comment puis-je agréger le total des notionnels par devise?

Donné:

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

Comment puis-je obtenir:

Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)
Était-ce utile?

La solution

J'ai écrit une simple opération de regroupement (en fait un trait Groupable avec une conversion implicite à partir d'un Iterable ) qui vous permettrait de: Regroupez vos trades par leur devise :

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
}

Donc Groupable fournit simplement un moyen d'extraire une clé de chaque élément d'un Iterable , puis de regrouper tous les éléments dont le même clé. Donc, dans votre cas:

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

Vous pouvez maintenant faire un mapElements assez simple ( mm est un Map ) et un foldLeft (ou /: - il est utile de comprendre l'opérateur foldLeft car il permet des agrégations extrêmement concises sur les collections) pour obtenir la somme:

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

Mes excuses si j'ai commis des erreurs dans cette dernière ligne. ts sont les valeurs de mm , qui sont (bien sûr) Iterable [Commerce] .

Autres conseils

Si vous utilisez le coffre, la machine est déjà là. groupBy est défini sur Traversable et la somme peut être appliquée directement à la liste, vous n'avez pas à écrire de pli.

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

À partir de Scala 2.13 , la plupart des collections sont fournies avec le groupMapReduce est une méthode qui est (comme son nom l'indique) un équivalent (plus efficace) d'un groupBy suivi de mapValues ?? et d'un réduire le pas:

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

Ceci:

    Les éléments
  • du groupe sont basés sur leur devise (partie du groupe du groupe MapReduce)

  • Mappez les valeurs groupées en fonction de leur montant (mappe du groupe Mappage Réduire)

  • réduisez les valeurs de ( _ + _ ) en les additionnant (réduction d'une partie de groupMap Réduire ).

Ceci est une version équivalente effectué en un seul passage dans la liste des:

trades.groupBy(_.currency).mapValues(_.map(_.amount).reduce(_+_))
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top