12

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

鉴于:

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

我怎样才能得到:

Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)
4

3 回答 3

17

如果您使用后备箱,机器已经在那里。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))
于 2009-06-22T22:22:28.680 回答
4

我编写了一个简单的 group-by 操作(实际上是 aGroupable trait与 an 的隐式转换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
}

因此Groupable,只需提供一种方法来从 an 中的每个项目中提取密钥Iterable,然后将具有相同密钥的所有此类项目分组。所以,在你的情况下:

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

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

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

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

于 2009-06-21T13:53:53.457 回答
1

开始Scala 2.13,大多数集合都提供了groupMapReduce方法,它(顾名思义)等效于(更有效)a和 reduce 步骤groupBymapValues

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

这:

  • groups 元素基于其货币(MapReduce 的组部分)

  • maps 分组值到它们的数量(映射组Map Reduce 的一部分)

  • reduces 值 ( _ + _) 通过对它们求和(减少 groupMap Reduce的一部分)。

这是一次通过以下列表执行的等效版本:

trades.groupBy(_.currency).mapValues(_.map(_.amount).reduce(_+_))
于 2018-10-03T21:46:41.790 回答