Котлин: сумма BigDecimal в списке

у меня есть список, который я хочу отфильтровать, а затем вернуть карту id с суммой сумм:

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

для java 8 это будет:

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

есть ли способ сделать это в Котлин?

3 ответов


так же, как вы использовали Collectors.reducing в Java, вы можете использовать fold или reduce функции расширения в Котлине:

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

вы можете создать свой собственный sumByBigDecimal функции расширения аналогично 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 } }

комбинируя подход сгиба и подход функции расширения, вы можете сделать следующее:

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 }