Scala中的聚合列表值

par*_*rkr 11 functional-programming scala scala-collections

从包含两个参数名义和货币​​的对象列表开始,我如何汇总每种货币的总名义?

鉴于:

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")
)
Run Code Online (Sandbox Code Playgroud)

我怎样才能得到:

Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)
Run Code Online (Sandbox Code Playgroud)

psp*_*psp 16

如果你使用行李箱,机器就已存在.groupBy是在Traversable上定义的,sum可以直接应用到列表中,你不必编写折叠.

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


oxb*_*kes 4

我编写了一个简单的分组操作(实际上Groupable trait是从 an 进行隐式转换的a Iterable),它允许您按以下方式对交易进行分组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
}
Run Code Online (Sandbox Code Playgroud)

因此Groupable,只需提供一种从 an 中的每个项目中提取密钥Iterable的方法,然后将具有相同密钥的所有此类项目分组即可。所以,就你而言:

//mm is a MultiMap[Currency, Trade]
val mm = trades groupBy { _.currency } 
Run Code Online (Sandbox Code Playgroud)

现在,您可以执行非常简单的mapElements( mmis a Map) 和 a foldLeft(or /:- 非常值得理解该foldLeft运算符,因为它可以对集合进行极其简洁的聚合) 来获取总和:

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

如果我在最后一行犯了一些错误,我深表歉意。ts是 的值mm,(当然)是Iterable[Trade]