使用两个条件过滤列表并创建地图标度
Filter list with two criteria and create a map scala
我有一个案例 class 可以保留交易(假设为资金转账)。为了简单起见,我将使用下面的一个
case class Trans(from: String, to: String, amount: Int)
我有交易清单
val tl = List(
Trans("a", "b", 30),
Trans("a", "c", 40),
Trans("b", "c", 10),
Trans("b", "a", 25),
Trans("c", "a", 15)
)
我想根据 from 和 to 对列表进行分组。然后需要将组的总和到一个Map。地图将元素保留在下面的结构中
("a" -> (70, 40))
// "a" is the key
// "70" is the sum of values where "a" is 'from' (30 + 40)
// "40" is the sum of values where "a" is 'to' (25 + 15)
例如,关于列表应将输出填充为
Map("a" -> (70, 40), "b" -> (35, 30), "c" -> (15, 50))
我可以用两个语句进行过滤,然后像下面这样组合它们
// out map
val mOut = tl.map(t => (t.from, t.amount)).groupBy(_._1).map(s => (s._1, s._2.sum))
// in map
val mIn = tl.map(t => (t.to, t.amount)).groupBy(_._1).map(s => (s._1, s._2.sum))
// finally I can join these tow maps to have the final output
有什么方法可以用 single/one 声明吗?
这似乎适用于单次遍历列表。
tl.foldLeft(Map.empty[String,(Int,Int)].withDefaultValue((0,0))){
case (m,Trans(from,to,amount)) =>
val (fromA, fromB) = m(from)
val (toA, toB) = m(to)
m + (from -> (fromA+amount, fromB)) + (to -> (toA, toB+amount))
}
//res0: Map[String,(Int, Int)] = Map(a -> (70,40), b -> (35,30), c -> (15,50))
希望你能为中间变量想出更好的名字。
如果您是 cats
或 scalaz
用户,您可以使用 foldMap
:
import cats._, implicits._ // or...
//import scalaz._, Scalaz._
case class Trans(from: String, to: String, amount: Int)
val tl = List(
Trans("a", "b", 30),
Trans("a", "c", 40),
Trans("b", "c", 10),
Trans("b", "a", 25),
Trans("c", "a", 15)
)
tl foldMap {
case Trans(from, to, am) => Map(from -> (am, 0), to -> (0, am))
}
你不需要 scalaz。 .
我想,你要找的是 flatMap
:
tl
.flatMap { p =>
p.from -> (p.amount, 0),
p.to -> (0, p.amount)
}.groupBy(_._1)
.mapValues { tuples =>
tuples.foldLeft((0,0)) { case ((a,b), (_, (c,d))) => (a+c, b+d) }
}
我有一个案例 class 可以保留交易(假设为资金转账)。为了简单起见,我将使用下面的一个
case class Trans(from: String, to: String, amount: Int)
我有交易清单
val tl = List(
Trans("a", "b", 30),
Trans("a", "c", 40),
Trans("b", "c", 10),
Trans("b", "a", 25),
Trans("c", "a", 15)
)
我想根据 from 和 to 对列表进行分组。然后需要将组的总和到一个Map。地图将元素保留在下面的结构中
("a" -> (70, 40))
// "a" is the key
// "70" is the sum of values where "a" is 'from' (30 + 40)
// "40" is the sum of values where "a" is 'to' (25 + 15)
例如,关于列表应将输出填充为
Map("a" -> (70, 40), "b" -> (35, 30), "c" -> (15, 50))
我可以用两个语句进行过滤,然后像下面这样组合它们
// out map
val mOut = tl.map(t => (t.from, t.amount)).groupBy(_._1).map(s => (s._1, s._2.sum))
// in map
val mIn = tl.map(t => (t.to, t.amount)).groupBy(_._1).map(s => (s._1, s._2.sum))
// finally I can join these tow maps to have the final output
有什么方法可以用 single/one 声明吗?
这似乎适用于单次遍历列表。
tl.foldLeft(Map.empty[String,(Int,Int)].withDefaultValue((0,0))){
case (m,Trans(from,to,amount)) =>
val (fromA, fromB) = m(from)
val (toA, toB) = m(to)
m + (from -> (fromA+amount, fromB)) + (to -> (toA, toB+amount))
}
//res0: Map[String,(Int, Int)] = Map(a -> (70,40), b -> (35,30), c -> (15,50))
希望你能为中间变量想出更好的名字。
如果您是 cats
或 scalaz
用户,您可以使用 foldMap
:
import cats._, implicits._ // or...
//import scalaz._, Scalaz._
case class Trans(from: String, to: String, amount: Int)
val tl = List(
Trans("a", "b", 30),
Trans("a", "c", 40),
Trans("b", "c", 10),
Trans("b", "a", 25),
Trans("c", "a", 15)
)
tl foldMap {
case Trans(from, to, am) => Map(from -> (am, 0), to -> (0, am))
}
你不需要 scalaz。 .
我想,你要找的是 flatMap
:
tl
.flatMap { p =>
p.from -> (p.amount, 0),
p.to -> (0, p.amount)
}.groupBy(_._1)
.mapValues { tuples =>
tuples.foldLeft((0,0)) { case ((a,b), (_, (c,d))) => (a+c, b+d) }
}