合并案例列表的元素 类
Merging elements of a list of case classes
我有以下情况class:
case class GHUser(login:String, contributions:Option[Int])
以及此类元素的列表:
val list = List(
List(GHUser("a", Some(10)), GHUser("b", Some(10))), List(GHUser("b", Some(300)))
).flatten
现在我想合并所有元素,以便为同一用户添加所有贡献。起初我以为我可以将 Monoid 应用于我的案例 class,像这样:
trait Semigroup[A] {
def combine(x: A, y: A): A
}
trait Monoid[A] extends Semigroup[A] {
def empty: A
}
case class GHUser(login: String, contributions: Option[Int])
object Main extends App {
val ghMonoid: Monoid[GHUser] = new Monoid[GHUser] {
def empty: GHUser = GHUser("", None)
def combine(x: GHUser, y: GHUser): GHUser = {
x match {
case GHUser(_, None) => GHUser(y.login, y.contributions)
case GHUser(_, Some(xv)) =>
y match {
case GHUser(_, None) => GHUser(x.login, x.contributions)
case GHUser(_, Some(yv)) => GHUser(x.login, Some(xv + yv))
}
}
}
}
val list = List(
List(GHUser("a", Some(10)), GHUser("b", Some(10))), List(GHUser("b", Some(300)))
).flatten
val b = list.groupBy(_.login)
val c = b.mapValues(_.foldLeft(ghMonoid.empty)(ghMonoid.combine))
println(c.valuesIterator mkString("\n"))
// GHUser(a,Some(10))
// GHUser(b,Some(310))
}
它有效,但我觉得我没有遵循幺半群定律,因为它要求所有用户都具有相同的 login
(因此我进行了 groupBy
调用。
是否有更清洁的解决方案?
更新
重读我的问题,似乎我不想要 Monoid
,而是 Semigroup
,对吗?
groupMapReduce()
(Scala 2.13) 可以满足您的大部分需求。
list.groupMapReduce(_.login)(_.contributions){case (a,b) => a.fold(b)(n => Some(n+b.getOrElse(0)))}
.map(GHUser.tupled)
//res0 = List(GHUser(a,Some(10)), GHUser(b,Some(310)))
Reduce
部分有点复杂,但它完成了工作。
这是一个简单的解决方案:
list.groupBy(_.login).map{
case (k, v) =>
GHUser(k, Some(v.flatMap(_.contributions).sum))
}
这将为没有贡献的用户提供 Some(0)
。如果你想要 None
在这种情况下它看起来更难看:
list.groupBy(_.login).map{
case (k, v) =>
val c = v.flatMap(_.contributions)
GHUser(k, c.headOption.map(_ => c.sum))
}
我有以下情况class:
case class GHUser(login:String, contributions:Option[Int])
以及此类元素的列表:
val list = List(
List(GHUser("a", Some(10)), GHUser("b", Some(10))), List(GHUser("b", Some(300)))
).flatten
现在我想合并所有元素,以便为同一用户添加所有贡献。起初我以为我可以将 Monoid 应用于我的案例 class,像这样:
trait Semigroup[A] {
def combine(x: A, y: A): A
}
trait Monoid[A] extends Semigroup[A] {
def empty: A
}
case class GHUser(login: String, contributions: Option[Int])
object Main extends App {
val ghMonoid: Monoid[GHUser] = new Monoid[GHUser] {
def empty: GHUser = GHUser("", None)
def combine(x: GHUser, y: GHUser): GHUser = {
x match {
case GHUser(_, None) => GHUser(y.login, y.contributions)
case GHUser(_, Some(xv)) =>
y match {
case GHUser(_, None) => GHUser(x.login, x.contributions)
case GHUser(_, Some(yv)) => GHUser(x.login, Some(xv + yv))
}
}
}
}
val list = List(
List(GHUser("a", Some(10)), GHUser("b", Some(10))), List(GHUser("b", Some(300)))
).flatten
val b = list.groupBy(_.login)
val c = b.mapValues(_.foldLeft(ghMonoid.empty)(ghMonoid.combine))
println(c.valuesIterator mkString("\n"))
// GHUser(a,Some(10))
// GHUser(b,Some(310))
}
它有效,但我觉得我没有遵循幺半群定律,因为它要求所有用户都具有相同的 login
(因此我进行了 groupBy
调用。
是否有更清洁的解决方案?
更新
重读我的问题,似乎我不想要 Monoid
,而是 Semigroup
,对吗?
groupMapReduce()
(Scala 2.13) 可以满足您的大部分需求。
list.groupMapReduce(_.login)(_.contributions){case (a,b) => a.fold(b)(n => Some(n+b.getOrElse(0)))}
.map(GHUser.tupled)
//res0 = List(GHUser(a,Some(10)), GHUser(b,Some(310)))
Reduce
部分有点复杂,但它完成了工作。
这是一个简单的解决方案:
list.groupBy(_.login).map{
case (k, v) =>
GHUser(k, Some(v.flatMap(_.contributions).sum))
}
这将为没有贡献的用户提供 Some(0)
。如果你想要 None
在这种情况下它看起来更难看:
list.groupBy(_.login).map{
case (k, v) =>
val c = v.flatMap(_.contributions)
GHUser(k, c.headOption.map(_ => c.sum))
}