Сводные значения списка в Scala - PullRequest
11 голосов
/ 21 июня 2009

Начиная со списка объектов, содержащих два условных параметра и валюту, как я могу агрегировать общее условное обозначение по валюте?

Дано:

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)

Ответы [ 3 ]

16 голосов
/ 23 июня 2009

Если вы используете багажник, оборудование уже есть. groupBy определен в Traversable и сумма может быть применена непосредственно к списку, вам не нужно писать сгиб.

scala> trades groupBy (_.currency) map { case (k,v) => k -> (v map (_.amount) sum) }
res1: Iterable[(String, Int)] = List((GBP,10001000), (JPY,10000100), (USD,10010000))
4 голосов
/ 21 июня 2009

Я написал простую операцию группировки (на самом деле Groupable trait с неявным преобразованием из 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 просто предоставляет способ извлечения ключа из каждого элемента в Iterable и затем группирует все такие элементы, которые имеют один и тот же ключ. Итак, в вашем случае:

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

Теперь вы можете делать довольно простые mapElements (mm - это Map) и foldLeft (или /:) - хорошо стоит понять оператор foldLeft, поскольку он обеспечивает чрезвычайно сжатые агрегации по коллекциям ) чтобы получить сумму:

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

Извините, если я сделал несколько ошибок в этой последней строке. ts - это значения mm, которые (конечно) Iterable[Trade].

0 голосов
/ 04 октября 2018

Начиная с Scala 2.13, большинство коллекций предоставляется с помощью метода groupMapReduce , который (как следует из его названия) эквивалентен (более эффективен) groupBy с последующим mapValues и шагом уменьшения :

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

Это:

  • group s элементов в зависимости от их валюты (групповая часть group MapReduce)

  • map s сгруппированные значения по их количеству (отображать часть группы Map Reduce)

  • reduce s значений (_ + _) путем их суммирования (уменьшить часть groupMap Reduce ).

Это эквивалентная версия , выполненная за один проход через Список:

trades.groupBy(_.currency).mapValues(_.map(_.amount).reduce(_+_))
...