Aggregate list values in Scala

前端 未结 3 2019
孤街浪徒
孤街浪徒 2021-02-02 18:08

Starting with a list of objects containing two parameters notional and currency, how can I aggregate the total notional per currency?

Given:

case class T         


        
相关标签:
3条回答
  • 2021-02-02 18:52

    I wrote a simple group-by operation (actually a Groupable trait with an implicit conversion from an Iterable) which would allow you to group your trades by their currency:

    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 is simply providing a way to extract a key from each item in an Iterable and then grouping all such items which have the same key. So, in your case:

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

    You can now do a quite simple mapElements (mm is a Map) and a foldLeft (or /: - well worth understanding the foldLeft operator as it enables extremely concise aggregations over collections) to get the sum:

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

    Apologies if I've made some mistakes in that last line. ts are the values of mm, which are (of course) Iterable[Trade].

    0 讨论(0)
  • 2021-02-02 18:54

    Starting Scala 2.13, most collections are provided with the groupMapReduce method which is (as its name suggests) an equivalent (more efficient) of a groupBy followed by mapValues and a reduce step:

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

    This:

    • groups elements based on their currency (group part of groupMapReduce)

    • maps grouped values to their amount (map part of groupMapReduce)

    • reduces values (_ + _) by summing them (reduce part of groupMapReduce).

    This is an equivalent version performed in one pass through the List of:

    trades.groupBy(_.currency).mapValues(_.map(_.amount).reduce(_+_))
    
    0 讨论(0)
  • 2021-02-02 19:05

    If you use trunk the machinery is already there. groupBy is defined on Traversable and sum can be applied directly to the list, you don't have to write a fold.

    scala> trades groupBy (_.currency) map { case (k,v) => k -> (v map (_.amount) sum) }
    res1: Iterable[(String, Int)] = List((GBP,10001000), (JPY,10000100), (USD,10010000))
    
    0 讨论(0)
提交回复
热议问题