【问题标题】:Kotlin: Sum of BigDecimal in a listKotlin:列表中 BigDecimal 的总和
【发布时间】:2016-09-30 14:14:33
【问题描述】:

我有一个要过滤的列表,然后返回一个带有金额总和的 id 映射:

val totalById = list
                    .filter { it.status == StatusEnum.Active }
                    .groupBy { it.item.id }
                    .mapValues { it.value.sumBy { it.amount } }

“it.amount”是 BigDecimal,但看起来 sumBy 只是 Int。

对于 java 8 它会是:

Collectors.groupingBy(i-> i.getItem().getId(), Collectors.mapping(Item::getAmount, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))))

有没有办法在 Kotlin 中做到这一点?

【问题讨论】:

    标签: kotlin


    【解决方案1】:

    就像您在 java 中使用 Collectors.reducing 一样,您可以在 Kotlin 中使用 foldreduce 扩展函数:

    val bigDecimals: List<BigDecimal> = ...
    val sum = bigDecimals.fold(BigDecimal.ZERO) { acc, e -> acc + e }
    // or
    val sum2 = bigDecimals.fold(BigDecimal.ZERO, BigDecimal::add)
    

    play.kotlinlang.org 上的完整示例:https://pl.kotl.in/o6pRhrqys

    Kotlin 1.4 开始,现在有一个函数 sumOf,它接受一个选择器函数并将该选择器返回的所有值相加,因此您可以按如下方式使用它:

    val totalById = list
                    .groupBy { it.id }
                    .mapValues { it.value.sumOf { it.amount } }
    

    play.kotlinlang.org 上的完整示例:https://pl.kotl.in/jXAfoyff-

    【讨论】:

    • 我试过了: .mapValues { it.value.map { it.amount}.fold(BigDecimal.ZERO, BigDecimal::add) 但编译器抱怨“::add”:以下功能都没有可以使用提供的参数调用:
    • [错误] 私人开放乐趣添加(p0:BigInteger!,p1:Int,p2:BigInteger!,p3:Int):BigDecimal!在 java.math.BigDecimal [错误] private open fun add(p0: Long, p1: Int, p2: BigInteger!, p3: Int): BigDecimal 中定义!在 java.math.BigDecimal [错误] private open fun add(p0: Long, p1: Int, p2: Long, p3: Int) 中定义:BigDecimal! java.math.BigDecimal [ERROR] private open fun add(p0: Long, p1: Long): Long 定义在 java.math.BigDecimal [ERROR] private open fun add(p0: Long, p1: Long, p2: Int): BigDecimal!在 java.math.BigDecimal 中定义
    • @klc,我已经制作了一个完整的示例,它可以工作:try.kotlinlang.org/#/UserProjects/cpohvnjtfr0uvpvcjc3gt02b1t/…,你可以在那里自己尝试。
    • 感谢您的示例,但我无法在 intellij 中编译它....将研究它....第一种方法有效!
    【解决方案2】:

    您可以创建自己的sumByBigDecimalextension function,类似于sumByDouble。例如:

    /**
     * Returns the sum of all values produced by [selector] function applied to each element in
     * the collection.
     */
    inline fun <T> Iterable<T>.sumByBigDecimal(selector: (T) -> BigDecimal): BigDecimal {
        var sum: BigDecimal = BigDecimal.ZERO
        for (element in this) {
            sum += selector(element)
        }
        return sum
    }
    

    示例用法:

    val totalById = list
            .filter { it.status == StatusEnum.Active }
            .groupBy { it.item.id }
            .mapValues { it.value.sumByBigDecimal { it.amount } }
    

    【讨论】:

    • 有没有办法让内联函数全局化?如您所见,这个 sumByBigDecimal 用于整个应用程序。
    • 函数默认是全局的@klc。它可以在任何地方导入和使用。
    【解决方案3】:

    在 Kotlin 1.4 中他们引入了sumOf,它的用法:

    val bigDecimals: List<BigDecimal> = ...
    val sum = bigDecimals.sumOf { it }
    

    documentation

    【讨论】:

    • 这应该是正确/最佳答案!谢谢。
    【解决方案4】:

    结合折叠方式和扩展函数方式,你可以这样做:

    fun Iterable<BigDecimal>.sumByBigDecimal(): BigDecimal {
        return this.fold(BigDecimal.ZERO) { acc, e -> acc + e }
    }
    
    fun <T> Iterable<T>.sumByBigDecimal(transform: (T) -> BigDecimal): BigDecimal {
        return this.fold(BigDecimal.ZERO) { acc, e -> acc + transform.invoke(e) }
    }
    

    像这样使用它们:

    listOfBigs.sumByBigDecimal()
    
    listOfWidgets.sumByBigDecimal { it.price }
    

    【讨论】:

      【解决方案5】:

      如果列表不为空,也可以使用reduce

      val bigDecimals: List<BigDecimal> = ...
      val sum = bigDecimals.reduce(BigDecimal::add)
      

      如果列表可以为空,则需要 foldBigDecimal.ZERO,就像接受的答案所暗示的那样。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-01
        • 2012-11-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多