Pergunta

A partir de uma lista de objetos que contêm dois parâmetros nocional e moeda, como posso agregar o nocional total por moeda?

Dada:

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

Como posso obter:

Map(JPY -> 10000100, USD -> 10010000, GBP -> 10001000)
Foi útil?

Solução

Eu escrevi um grupo-by simples operação (na verdade um Groupable trait com uma conversão implícita de um Iterable) que lhe permitiria agrupar seus comércios por sua 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
}

Assim Groupable é simplesmente fornecer uma maneira de extrair um tecla de cada item em um Iterable e, em seguida, agrupar todos esses itens que têm a mesma chave. Assim, no seu caso:

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

Você pode agora fazer um mapElements bastante simples (mm é um Map) e um foldLeft (ou /: - vale bem a compreensão do operador foldLeft pois permite agregações extremamente concisos sobre coleções) para obter a soma:

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

Desculpas se eu cometi alguns erros nessa última linha. ts são os valores de mm, que são (é claro) Iterable[Trade].

Outras dicas

Se você usar tronco a maquinaria já está lá. groupBy é definido em Traversable e soma pode ser aplicada diretamente para a lista, você não tem que escrever uma dobra.

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 de partida, a maioria das coleções são fornecidos com o groupMapReduce método é que (como o nome sugere) um equivalente (mais eficaz) de um groupBy seguido por mapValues e uma redução passo:

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

Este:

  • groups elementos com base em sua moeda (parte do grupo de grupo MapReduce)

  • maps valores agrupados ao seu montante (mapa parte do grupo Map Reduzir)

  • reduces valores (_ + _) pela soma deles (reduzir parte de groupMap Reduzir ).

Esta é uma versão equivalente realizada em uma passagem através da Lista de:

trades.groupBy(_.currency).mapValues(_.map(_.amount).reduce(_+_))
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top