Значения агрегированного списка в Scala
-
06-07-2019 - |
Вопрос
Начиная со списка объектов, содержащих два параметра - условное обозначение и валюту, как я могу агрегировать общее условное обозначение по валюте?
Данный:
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)
Решение
Я написал простую групповую операцию (на самом деле 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
) и a foldLeft
(или /:
- хорошо стоит понимать, что foldLeft
оператор, поскольку он позволяет выполнять предельно сжатые агрегации по коллекциям), чтобы получить сумму:
val sums: Map[Currency, Int] = mm mapElements { ts =>
(0 /: ts) { (sum,t) => sum + t.notional }
}
Прошу прощения, если я допустил какие-то ошибки в этой последней строке. ts
являются значениями mm
, которые (конечно) Iterable[Trade]
.
Другие советы
Если вы используете trunk, то оборудование уже там.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))
Начиная Scala 2.13
, большинство коллекций снабжены Групповая карта создания метод, который является (как следует из его названия) эквивалентом (более эффективным) groupBy
за которым следует mapValues
и шаг уменьшения:
trades.groupMapReduce(_.currency)(_.amount)(_ + _)
// immutable.Map[String,Int] = Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)
Это:
group
элементы s, основанные на их валюте (групповая часть ГруппаMapReduce)map
s сгруппированные значения в соответствии с их суммой (сопоставьте часть группыКартаУменьшить)reduce
значения s (_ + _
) путем их суммирования (уменьшить часть GroupMapУменьшить).
Это эквивалентная версия выполняется за один проход через список:
trades.groupBy(_.currency).mapValues(_.map(_.amount).reduce(_+_))