스칼라의 집계 목록 값
-
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)
해결책
나는 간단한 그룹 바이 운영을 썼습니다 (실제로 a Groupable
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
(mm
a Map
) 그리고 a foldLeft
(또는 /:
- 이해할 가치가 있습니다 foldLeft
연산자는 컬렉션을 통해 매우 간결한 집계를 가능하게 할 수 있으므로) : 합계를 얻을 수 있습니다.
val sums: Map[Currency, Int] = mm mapElements { ts =>
(0 /: ts) { (sum,t) => sum + t.notional }
}
마지막 줄에서 실수를 저지른 경우 사과드립니다. ts
값입니다 mm
, (물론) Iterable[Trade]
.
다른 팁
트렁크를 사용하면 기계가 이미 있습니다. Groupby는 트래버스 가능에 정의되어 있으며 합계를 목록에 직접 적용 할 수 있으므로 접을 필요가 없습니다.
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
, 대부분의 컬렉션은 다음과 같이 제공됩니다 GroupMapReduce (이름을 알 수 있듯이)의 방법은 groupBy
그 뒤에 mapValues
그리고 감소 단계 :
trades.groupMapReduce(_.currency)(_.amount)(_ + _)
// immutable.Map[String,Int] = Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)
이것:
group
통화에 따른 요소 (그룹 부분 그룹Mapreduce)map
S 그룹의 금액에 그룹화 된 값 (그룹의 맵 부분지도줄이다)reduce
s 값 (_ + _
) 합산함으로써 (GroupMap의 일부를 줄입니다줄이다).
이것은 동등한 버전입니다 한 번의 패스로 수행됩니다 목록을 통해 :
trades.groupBy(_.currency).mapValues(_.map(_.amount).reduce(_+_))