← Назад к вопросам

Можно ли использовать extension функцию для подсчета суммы по какому-то предикату в коллекции ключ-значение в Kotlin?

2.0 Middle🔥 101 комментариев
#Kotlin основы#Коллекции и структуры данных

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Ответ

Да, безусловно можно и очень удобно использовать extension-функции в Kotlin для подсчета суммы по предикату в коллекциях типа "ключ-значение". Это классический пример применения Kotlin DSL (Domain Specific Language) подходов для создания читаемого и переиспользуемого кода. Рассмотрим несколько практических вариантов.

Основные подходы с extension-функциями

1. Общий extension для Map

Создадим универсальную extension-функцию для Map<K, V>, где значение поддерживает арифметические операции:

fun <K, V : Number> Map<K, V>.sumByPredicate(predicate: (Map.Entry<K, V>) -> Boolean): Double {
    return this.entries
        .filter(predicate)
        .sumOf { it.value.toDouble() }
}

// Использование
val map = mapOf("a" to 10, "b" to 20, "c" to 30, "d" to 5)

// Сумма значений, где ключ содержит букву 'a' или значение > 15
val result1 = map.sumByPredicate { it.key.contains("a") || it.value > 15 }
println(result1) // 60.0 (10 + 20 + 30)

// Сумма значений с четными ключами (если ключи - числа)
val map2 = mapOf(1 to 100, 2 to 200, 3 to 300)
val result2 = map2.sumByPredicate { it.key % 2 == 0 }
println(result2) // 200.0

2. Более специфичная версия для определенных типов

Если работаем преимущественно с определенными типами, можно создать более типобезопасные extensions:

fun Map<String, Int>.sumWhereKeyMatches(regex: Regex): Int {
    return this.filter { regex.matches(it.key) }
        .values.sum()
}

fun Map<String, Double>.sumWhereValueGreaterThan(threshold: Double): Double {
    return this.filter { it.value > threshold }
        .values.sum()
}

// Использование
val prices = mapOf("apple" to 2.5, "banana" to 1.8, "avocado" to 3.2, "berry" to 4.0)

val fruitsStartingWithA = prices.sumWhereKeyMatches("^a.*".toRegex())
println(fruitsStartingWithA) // 5.7 (apple + avocado)

val expensiveFruits = prices.sumWhereValueGreaterThan(3.0)
println(expensiveFruits) // 7.2 (avocado + berry)

3. Infix-нотация для читаемости

Можно использовать infix-функции для создания DSL-подобного синтаксиса:

infix fun Map<String, Number>.sumWhere(condition: (String, Number) -> Boolean): Double {
    return this.filter { condition(it.key, it.value) }
        .values.sumOf { it.toDouble() }
}

// Использование с infix-нотацией (если функция принимает лямбду последним параметром)
val scores = mapOf("John" to 85, "Jane" to 92, "Bob" to 76, "Alice" to 95)

val highScores = scores.sumWhere { name, score -> 
    score.toInt() > 80 && name.startsWith("J") 
}
println(highScores) // 177.0 (John + Jane)

4. Комбинирование с другими extension-функциями

// Дополнительные полезные extensions
fun <K> Map<K, Int>.averageByPredicate(predicate: (Map.Entry<K, Int>) -> Boolean): Double {
    val filtered = this.entries.filter(predicate)
    return if (filtered.isEmpty()) 0.0 else filtered.sumOf { it.value } / filtered.size.toDouble()
}

fun Map<String, List<Int>>.sumOfAllWhere(predicate: (String) -> Boolean): Int {
    return this.filter { predicate(it.key) }
        .flatMap { it.value }
        .sum()
}

// Использование
val teamScores = mapOf(
    "developers" to listOf(85, 90, 78),
    "managers" to listOf(92, 88),
    "qa" to listOf(76, 82, 79)
)

val devScoresSum = teamScores.sumOfAllWhere { it.contains("dev") }
println(devScoresSum) // 253 (85+90+78)

Преимущества такого подхода:

  • Читаемость кода: map.sumByPredicate { ... } читается практически как обычное предложение
  • Переиспользование: один раз написали - используем во всем проекте
  • Типобезопасность: компилятор проверит типы на этапе компиляции
  • Интеграция с Kotlin stdlib: отлично сочетается со стандартными функциями вроде filter, map, sumOf
  • Поддержка IDE: автодополнение и подсказки параметров работают отлично

Рекомендации по использованию:

  1. Именование: используйте понятные имена в стиле sumBy..., sumWhere..., totalFor...
  2. Производительность: для больших коллекций учитывайте, что некоторые реализации создают промежуточные коллекции
  3. Null-безопасность: предусматривайте обработку null-значений если коллекция может содержать null
  4. Generic-типы: используйте обобщенные типы для максимальной гибкости, но сохраняйте контекстную полезность

Альтернатива без extension-функций (для сравнения):

// Без extension
val sum = map.entries.filter { it.value > 10 }.sumOf { it.value }

// С extension - более выразительно
val sum = map.sumWhereValueGreaterThan(10)

Вывод: Использование extension-функций для работы с коллекциями "ключ-значение" в Kotlin не только возможно, но и является рекомендуемой практикой, соответствующей философии языка — писать выразительный, безопасный и поддерживаемый код. Это позволяет создавать предметно-ориентированный язык для вашей конкретной предметной области, что особенно ценно в больших проектах.

Можно ли использовать extension функцию для подсчета суммы по какому-то предикату в коллекции ключ-значение в Kotlin? | PrepBro