Суммируйте подмножество чисел в списке

Есть ли способ в Kotlin для выполнения операции sum() в отфильтрованном списке чисел, без фактической фильтрации элементов сначала?

Я ищу что-то вроде этого:

val nums = listOf<Long>(-2, -1, 1, 2, 3, 4)
val sum = nums.sum(it > 0)

Ответ 1

Вы можете использовать Iterable<T>.sumBy:

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

Вы можете передать ей функцию, где функция преобразует отрицательное значение в 0. Таким образом, она суммирует все значения в списке, который больше 0, поскольку добавление 0 не влияет на результат.

val nums = listOf<Long>(-2, -1, 1, 2, 3, 4)
val sum = nums.sumBy { if (it > 0) it.toInt() else 0 }
println(sum)    //10

Если вам требуется вернуть значение Long, вы должны написать расширение для Long так же, как Iterable<T>.sumByDouble.

inline fun <T> Iterable<T>.sumByLong(selector: (T) -> Long): Long {
    var sum: Long = 0
    for (element in this) {
        sum += selector(element)
    }
    return sum
}

Затем конверсию toInt() можно убрать.

 nums.sumByLong { if (it > 0) it else 0 }

Как подсказывает @Ruckus T-Boom, if (it > 0) it else 0 можно упростить, используя Long.coerceAtLeast(), который возвращает само значение или заданное минимальное значение:

nums.sumByLong { it.coerceAtLeast(0) }

Ответ 2

data class Product(val name: String, val quantity: Int) {
}

fun main(args: Array<String>) {

    val productList = listOf(
            Product("A", 100),
            Product("B", 200),
            Product("C", 300)
    )

    val totalPriceInList1: Int = productList.map { it.quantity }.sum()
    println("sum(): " + totalPriceInList1)

    val totalPriceInList2: Int = productList.sumBy { it.quantity }
    println("sumBy(): " + totalPriceInList2)
} 

это результат нашего кода

sum(): 600
sumBy(): 600