【问题标题】:Merging elements of a list of case classes合并案例类列表的元素
【发布时间】:2019-10-30 09:12:43
【问题描述】:

我有以下案例类:

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 应用于我的案例类,如下所示:

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

它有效,但我觉得我没有遵循 Monoid Laws,因为它要求所有用户都有相同的login(因此我拨打了groupBy 电话。

有更清洁的解决方案吗?

更新

重读我的问题,似乎我不需要Monoid,而是Semigroup,对吗?

【问题讨论】:

  • 与简单的groupBy - map - fold 相比,这些似乎非常重量级。
  • 不知道为什么,在groupby上不能调用map:b.map(x => x),x好像是Nothing

标签: scala monoids


【解决方案1】:

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 部分有点复杂,但它完成了工作。

【讨论】:

    【解决方案2】:

    这是一个简单的解决方案:

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

    【讨论】:

    • 谢谢!Some(0) 没问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    • 2020-08-03
    • 2016-08-15
    • 2021-06-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多