【问题标题】:GroupBy multiple columns as key and sum multiple columns like sql?GroupBy 多列作为键并对多列求和,如 sql?
【发布时间】:2019-07-08 19:59:32
【问题描述】:

我使用的是 scala 2.12。

我有一个案例类如下:

case class MyClass(date: java.util.Date, book: String, priceLocal: Double, priceConv: Double)

我可以根据日期和书籍进行分组。

例如,对于:

val listOfMyClass = List(
  MyClass(20190708, "book1", 100, 120),
  MyClass(20190708, "book1", 200, 220),
  MyClass(20190708, "book2", 50, 60),
  MyClass(20190708, "book2", 60, 70)
)

val groupedData = listOfMyClass.groupBy(t => (t.date, t.book))

我想要 SQL 中的数据:

(20190708, "book1", 300, 340)
(20190708, "book2", 110, 130)

我可以对一列进行映射和求和,但不能同时使用这两列。

val groupedDataSum = listOfMyClass.groupBy(t => (t.date, t.book)).mapValues(_.map(_.priceLocal).sum)

但是如何将第二列也用作总和?

【问题讨论】:

    标签: scala group-by


    【解决方案1】:

    您可以混合使用groupBy(按日期和书籍对元素进行分组)和reduce 来累积分组值:

    // val list = List(
    //   MyClass(Date(2019, 7, 8), "book1", 100, 120),
    //   MyClass(Date(2019, 7, 8), "book1", 200, 220),
    //   MyClass(Date(2019, 7, 8), "book2", 50, 60),
    //   MyClass(Date(2019, 7, 8), "book2", 60, 70)
    // )
    list
      .groupBy { case MyClass(date, book, _, _) => (date, book) }
      .mapValues { values =>
        values
          .map { case MyClass(_, _, priceLocal, priceConv) => (priceLocal, priceConv) }
          .reduce((x, y) => (x._1 + y._1, x._2 + y._2))
      }
      .map { case ((date, book), (priceLocal, priceConv)) =>
        (date, book, priceLocal, priceConv)
      }
    // List(
    //   (Date(2019, 7, 8), "book1", 300, 340),
    //   (Date(2019, 7, 8), "book2", 110, 130)
    // )
    

    这个:

    • 按日期和书籍对字符进行分组 (groupBy)

    • 按以下方式映射每个分组值 (mapValues):

      • 将值映射为价格元组
      • 并通过逐部分求和来减少这些元组
    • 将元组(日期、书籍)映射到元组(价格、价格)到 4 个元素的元组

    【讨论】:

    • 谢谢,这帮助很大。我现在可以使用了。
    【解决方案2】:

    您可以将priceLocal 和priceConv 设为元组,然后按元素设置reduce 来对各个元组元素求和:

    listOfMyClass.groupBy(t => (t.date, t.book)).mapValues(
      _.map(s => (s.priceLocal, s.priceConv)).
        reduce((acc, x) => (acc._1 + x._1, acc._2 + x._2))
    )
    

    【讨论】:

    • 感谢大家的协助。在我的情况下减少将起作用。接受第一个答案,因为我尝试了它并且它的工作。
    【解决方案3】:

    mapValues 后跟 reduce 应该可以解决问题。这是一个示例代码。

      val grouped = listOfMyClass.groupBy(t => (t.date, t.book))
        .mapValues(lst => lst.reduce((m1, m2) => 
          MyClass(m1.date, m1.book, m1.priceLocal + m2.priceLocal, m1.priceConv + m2.priceConv))).values
    

    这会返回一个迭代器到 MyClass 实例的简化列表。

    【讨论】:

    • 感谢大家的协助。在我的情况下减少将起作用。接受第一个答案,因为我尝试了它并且它的工作。
    【解决方案4】:

    要获得您说需要的类似 SQL 的输出,您只需对 mapValues 和 reduce 操作生成的 Map[(Date,String],(Double, Double)] 执行最后一个映射。

    listOfMyClass groupBy(a => (a.date, a.book)) 
    mapValues(a => a.map(e => (e.priceConv, e.priceLocal)) reduce((a,b) => (a._1+b._1, a._2+b._2)))
    map (x => (x._1._1, x._1._2, x._2._1, x._2._1)) //final map will give you the SQL-type output you were looking for
    

    【讨论】:

    • 感谢大家的协助。在我的情况下减少将起作用。接受第一个答案,因为我尝试了它并且它的工作。
    猜你喜欢
    • 1970-01-01
    • 2018-07-23
    • 2019-12-26
    • 2017-04-29
    • 1970-01-01
    • 2021-12-20
    • 1970-01-01
    • 2012-01-03
    相关资源
    最近更新 更多