Función reduce En Kotlin

En este tutorial te mostraremos el uso de la función reduce en Kotlin, para retornar el acumulado de los resultados obtenidos, al aplicar una operación personalizada a los valores de una colección.

Función reduce()

La función reduce() aplica una operación a cada elemento de una colección de izquierda a derecha y al final retorna el valor acumulado.

// reduce() en arreglos genéricos
inline fun <S, T : S> Array<out T>.reduce(
    operation: (acc: S, T) -> S
): S

// reduce() en iterables
inline fun <S, T : S> Iterable<T>.reduce(
    operation: (acc: S, T) -> S
): S

// reduce() en instancias Grouping
inline fun <S, T : S, K> Grouping<T, K>.reduce(
    operation: (key: K, accumulator: S, element: T) -> S
): Map<K, S>

Esta toma como argumento una función de operación (operation), cuyos parámetros son el valor acumulado (acc) y el elemento actual. El resultado del cuerpo será luego pasado como valor de acumulado en el elemento de la derecha.

Considera la siguiente ilustración donde se muestra la aplicación de reduce() sobre el rango entero 1..5. La idea es multiplicar cada número con su consecutivo:

Ejemplo de función reduce en Kotlin

Como ves, el primer valor de product es el elemento de la posición inicial del rango. En el segundo paso value será el elemento consecutivo 2 y así sucesivamente, hasta que product acumule el valor 120 para el número 5.

La representación del ejemplo anterior puedes escribirla en Kotlin de la siguiente forma:

fun main() {
    val oneToFive = 1..5
    val sequenceProduct = oneToFive.reduce { product, value -> product * value }
    println("El producto es $sequenceProduct")
}

Salida:

El producto es 120

Función reduceRight()

Como lo indica su nombre, reduceRight() realiza la acumulación sobre las colecciones de derecha a izquierda. Se aplica la función de operación comenzando desde el último elemento hasta llegar al primero. La siguiente es la definición para los arreglos genéricos.

inline fun <S, T : S> Array<out T>.reduceRight(
    operation: (T, acc: S) -> S
): S

La función reduceRight() invierte el orden de los parámetros de la función operation. Tomemos como ejemplo la obtención de la diferencia acumulada de un arreglo de enteros que debe tener sus elementos ordenados de menor a mayor.

fun main() {
    val numbers = intArrayOf(6, 1, 4, 2, 1, 20, 1)
    val differenceAcc = numbers.sorted().reduceRight { value, acc -> acc - value }
    println("La diferencia es $differenceAcc")
}

Salida:

La diferencia es 5

En este ejemplo antes de llamar a reduceRight(), se llama a la función sorted() para ordenar el arreglo de menor. En esencia, la diferencia acumulada es la sustracción entre todos los valores del arreglo y el número 20 que es el mayor.

Usar Índices En reduce()

Para usar los índices en la acumulación usa las funciones reduceIndexed() y reduceRightIndexed(). Ambas realizan el mismo proceso que sus versiones cortas, solo que la función de operación tiene al índice del elemento como tercer parámetro.

Por ejemplo, a partir de una lista de Strings, construir un mensaje mostrando el índice con las palabras:

fun main() {
    val words = listOf("Inicio:", "Lámpara", "Computadora", "Bañera")
    val evenWordsMsg = words.reduceIndexed { index, acc, s -> "$acc\n$s en $index" }
    println(evenWordsMsg)
}

Salida:

Inicio:
Lámpara en 1
Computadora en 2
Bañera en 3

Como ves, el primer elemento como es dispuesto como inicio del acumulado, no tendrá representación de índice.

Funciones reduce() Con Retorno null

Las funciones anteriores lanzan una excepción si la colección invocadora es vacía. Por ejemplo:

fun main() {
    println(emptyList<Int>().reduce { acc, i -> acc + 3 * i })
}

Salida:

Exception in thread "main" java.lang.UnsupportedOperationException: Empty collection can't be reduced.

Si quieres soportar en tu programa la posibilidad de que sea vacía, entonces puedes usar las variaciones de reduce() para obtener null en este caso.

  • reduceOrNull()
  • reduceRightOrNull()
  • reduceIndexedOrNull()
  • reduceRightIndexedOrNull()

Si reescribes el ejemplo anterior con reduceOrNull() el resultado será null:

fun main() {
    println(emptyList<Int>().reduceOrNull { acc, i -> acc + 3 * i }) // null
}

Función runningReduce()

Y por último, si deseas sostener los valores acumulativos del recorrido de la colección, entonces usa la función runningReduce(). Esta retorna en una lista con todos los valores resultantes de aplicar la función de operación. Adicionalmente, puedes usar su variación con índices runningReduceIndexed().

Por ejemplo, a partir de la lista (1, 2, 3, 5, 10), calcular la sumatoria de las expresiones S = 3 * v y S = i * v, donde v es un valor de la lista e i el índice del dicho valor. Se debe mostrar el valor acumulado en cada paso:

fun main() {
    val values = listOf(1, 2, 3, 5, 10)
    val reduceSteps = values.runningReduce { acc, i -> acc + 3 * i }
    println(reduceSteps)

    val reduceIndexedSteps = values.runningReduceIndexed { index, acc, i -> acc + index * i }
    println(reduceIndexedSteps)
}

Salida:

[1, 7, 16, 31, 61]
[1, 3, 9, 24, 64]

Únete Al Discord De Develou

Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!