Operaciones con colecciones VIII: obtener valores de agrupación
Continuando la serie sobre operaciones con colecciones, en esta entrada vamos a ver un conjunto de funciones que realizan operaciones (de agrupación o agregación, del inglés aggregate operations) para calcular ciertos valores o índices de agrupación a partir de los datos de la colección, como por ejemplo, los valores mínimo y máximo, el número de elementos, la suma de todos los elementos, la media o la suma acumulada. Un ejemplo de operación de agrupamiento es el cálculo del promedio de temperatura a partir de los valores de temperatura diaria durante un mes.
Ejemplos de este tipo de funciones son:
- min() y max(), que devuelven el elemento más pequeño y el más grande respectivamente.
- average(), que devuelve el valor promedio en una colección de números.
- sum(), que devuelve la suma de elementos en una colección de números.
- count(), que devuelve el número de elementos en una colección.
fun main() { val gradosC = listOf(25.5, 26.1, 24.3, 25.2) println("Datos: ${gradosC.count()}") println("Máximo: %.1f".format(gradosC.max())) println("Mínimo: %.1f".format(gradosC.min())) println("Media: %.1f".format(gradosC.average())) println("Suma: %.1f".format(gradosC.sum())) }
También hay otras funciones que sirven para recuperar los elementos más pequeños y más grandes mediante funciones de selección o mediante un comparador personalizado, como son:
- maxBy() y minBy(), que mediante una función de selección devuelven el elemento con el valor más grande o más pequeño (o en caso de igualdad, el primero que encuentra).
- maxWith() y minWith(), que tomando como referencia un objeto de tipo Comparator devuelven el elemento más grande o más pequeño (o en caso de igualdad, el primero que encuentra).
fun main() { val numeros = listOf(1, 2, 3, 4, 5) val moduloDos = numeros.minBy { it % 2 } println(moduloDos) // 2 val strings = listOf("uno", "dos", "tres", "cuatro", "cinco") val masLargo = strings.maxWith(compareBy { it.length }) println(masLargo) // cuatro }
Además, hay funciones de suma avanzada o compleja que toman una función que opera sobre los elementos y devuelven la suma total de los valores de retorno:
- sumBy() se aplica a funciones que devuelven valores de tipo Int en los elementos de la colección.
- sumByDouble() funciona con funciones que devuelven valores de tipo Double.
val numeros = listOf(1, 2, 3, 4) println(numeros.sumBy { it * 2 }) // 20 println(numeros.sumByDouble { it.toDouble() / 2 }) // 5.0
Por otro lado, existen las funciones reduce() y fold() que aplican la operación proporcionada a los elementos de la colección de forma secuencial y devuelven el resultado acumulado. La operación toma dos argumentos: el valor acumulado previamente y el elemento de colección, con la diferencia de que fold() toma un valor inicial y lo usa como el valor acumulado en el primer paso, mientras que reduce() usa en el primer paso el primer y el segundo elementos como argumentos de la operación. Más claro en un ejemplo comentado:
fun main() { val numeros = listOf(5, 2, 10, 4) val acumulado = numeros.reduce { sum, element -> sum + element } println(acumulado) // 5 -> (5 + 2) -> (7 + 10) -> (17 + 4) = 21 val desdeDiez = numeros.fold(10) { sum, element -> sum + element } println(desdeDiez) // 10 + 5 -> (15 + 2) -> (17 + 10) -> (27 + 4) = 31 val acumuladoDoble = numeros.fold(0) { sum, element -> sum + element * 2 } println(acumuladoDoble) // 5x2 -> (10 + 2x2) -> (14 + 10x2) -> (34 + 4x2) = 42 // con reduce el primer elemento no se multiplica por 2: val acumuladoDobleReduce = numeros.reduce { sum, element -> sum + element * 2 } println(acumuladoDobleReduce) // 5 -> (5 + 2x2) -> (9 + 10x2) -> (29 + 4x2) = 37 }
Para aplicar estas funciones en el orden inverso, usamos las funciones reduceRight() y foldRight(), que operan de manera similar a reduce() y fold() pero comienzan desde el último elemento y avanzan hacia el anterior hasta el primero (además hay que tener en cuenta que en estas funciones los argumentos de la operación cambian su orden: primero va el elemento y luego el valor acumulado).
También podemos realizar operaciones que toman los índices de los elementos como parámetros con las funciones reduceIndexed() y foldIndexed() pasando el índice del elemento como primer argumento de la operación (y las funciones inversas que aplican las operaciones a los elementos de la colección de derecha a izquierda: reduceRightIndexed() y foldRightIndexed()).
fun main() { val numeros = listOf(5, 2, 10, 4) val sumaPosicionesPares = numeros.foldIndexed(0) { idx, suma, elemento -> if (idx % 2 == 0) suma + elemento else suma } println(sumaPosicionesPares) // 5 -> 5 -> (5 + 10) -> 15 = 15 // idx = 0 -> 5 (suma + elemento) // idx = 1 -> 5 (suma) // idx = 2 -> 5 + 10 (suma + elemento) // idx = 3 -> 15 (suma) }
Comentarios
Publicar un comentario