Función groupingBy En Kotlin

ANUNCIO
Loading...

En este tutorial verás cómo usar a la función groupingBy en Kotlin para agrupar los elementos de una colección y luego aplicarles una operación a todos los grupos generados, generando así un mapa con los resultados por cada grupo.

Función groupingBy()

La función de extensión groupingBy() funciona similar a groupBy(). Ambas toman una función lambda que hace de selector de clave y así crear grupos de elementos con dichas claves.

La diferencia está en que groupingBy() retorna en una instancia del tipo Grouping. Esta interfaz te permite aplicar funciones de agregación sobre los grupos, las cuales retornan un mapa con los valores finales de cada grupo:

// groupingBy() en arreglos inline fun <T, K> Array<out T>.groupingBy( crossinline keySelector: (T) -> K ): Grouping<T, K> // groupingBy() en iterables inline fun <T, K> Iterable<T>.groupingBy( crossinline keySelector: (T) -> K ): Grouping<T, K>
Lenguaje del código: Kotlin (kotlin)

Las funciones que puedes usar sobre el resultado de groupingBy() son:

  • eachCount(): Cuentas los elementos por cada grupo. El resultado en un mapa con pares cuyo valor es la cantidad contada
  • fold(): Aplica una operación acumulativa a partir de los valores de cada grupo. El valor inicial del acumulado lo pasas como primer parámetro
  • reduce(): Aplicar una operación que reduce a una instancia como resultado de cada grupo
  • aggregate(): Esta función es la base para generar las otras funciones nombradas. Toma una operación y la aplica sobre cada elemento para obtener el resultado acumulado

Contar El Número De Empleados En Cada Departamento

Tomemos como ejemplo un sencillo diseño para modelar a los empleados de una compañía que pertenecen a un departamento.

data class Employee(val name: String, val salary: Int, val department: String) { override fun toString(): String { return "%-10s %4s %s".format(name, salary, department) } } val employees = listOf( Employee("Carolina", 2000, "Finanzas"), Employee("Shirley", 1500, "Recursos humanos"), Employee("Carlos", 1250, "Finanzas"), Employee("Jill", 4200, "Marketing"), Employee("Pedro", 500, "Logística"), Employee("Ana", 550, "Logística"), Employee("Santiago", 1200, "Logística"), ) fun main() { employees.forEach(::println) }
Lenguaje del código: Kotlin (kotlin)

Salida:

Carolina 2000 Finanzas Shirley 1500 Recursos humanos Carlos 1250 Finanzas Jill 4200 Marketing Pedro 500 Logísticaa Ana 550 Logísticaa Santiago 1200 Logísticaa
Lenguaje del código: Kotlin (kotlin)

La clase Employee se constituye del nombre, salario y departamento a que pertenece el empleado. Y como ves la lista employees contiene siete empleados que usaremos para probar las diferentes funciones asociadas a Grouping.

El primer ejemplo será contar la cantidad de empleados que existen por departamento actualmente. Esto significa que debemos agrupar a la lista por la propiedad department y luego aplicar eachCount():

fun <T, K> Grouping<T, K>.eachCount(): Map<K, Int>
Lenguaje del código: Kotlin (kotlin)

La función eachCount() retorna un mapa con pares cuyos valores son la cantidad contada por cada clave.

fun main() { val employeesByDepartment = employees.groupingBy(Employee::department).eachCount() println("Cantidad de empleados por departamento:") employeesByDepartment.forEach { (department, count) -> println("$department tiene $count empleados") } }
Lenguaje del código: Kotlin (kotlin)

Salida:

Cantidad de empleados por departamento: Finanzas tiene 2 empleados Recursos humanos tiene 1 empleados Marketing tiene 1 empleados Logística tiene 3 empleados

Calcular La Suma De Salarios Por Departamento

Para computar la suma total en cada departamento recurrimos de nuevo a la agrupación de la lista employees por la propiedad department.

La operación de suma es posible conseguirla a través del método fold() o agreggate(). Nuestro objetivo es acumular la suma de la propiedad salary en cada grupo existente.

inline fun <T, K, R> Grouping<T, K>.fold( initialValue: R, operation: (accumulator: R, element: T) -> R ): Map<K, R>
Lenguaje del código: Kotlin (kotlin)

Veamos la solución:

fun main() { val salariesSumByDepartment = employees .groupingBy(Employee::department) .fold(0) { sum, employee -> sum + employee.salary } println("Suma total de salarios por departamento:") salariesSumByDepartment.forEach { (department, salarySum) -> println("$department: $$salarySum ") } }
Lenguaje del código: Kotlin (kotlin)

Salida:

Suma total de salarios por departamento: Finanzas: $3250 Recursos humanos: $1500 Marketing: $4200 Logística: $2250

Obtener El Salario Más Alto Por Departamento

En este caso la operación de acumulación que necesitamos no es aritmética, si no de reducción. Necesitamos imprimir un solo valor seleccionado por cada departamento luego de usar groupingBy().

La solución consta del uso de la función reduce(), la cual nos fuerza a elegir una instancia de Employee en la función lambda de operación.

inline fun <S, T : S, K> Grouping<T, K>.reduce( operation: (key: K, accumulator: S, element: T) -> S ): Map<K, S>
Lenguaje del código: Kotlin (kotlin)

Como ves en la sintaxis, el acumulado comparte al tipo S en común con element. El cuerpo de la operación asigna al acumulado el empleado que se considere preponderante en cuanto a salario:

fun main() { val salariesSumByDepartment = employees .groupingBy(Employee::department) .reduce { _, highestSalaryEmployee, employee -> if (highestSalaryEmployee.salary > employee.salary) highestSalaryEmployee else employee } salariesSumByDepartment.forEach { (department, employee) -> println("${employee.salary} es el mayor salario en $department") } }
Lenguaje del código: Kotlin (kotlin)

Salida:

2000 es el mayor salario en Finanzas 1500 es el mayor salario en Recursos humanos 4200 es el mayor salario en Marketing 1200 es el mayor salario en Logística

La Función aggregate()

La función aggregate() recibe una función de operación para acumular valores en un mapa con respecto a los grupos que se generan de Grouping.

inline fun <T, K, R> Grouping<T, K>.aggregate( operation: (key: K, accumulator: R?, element: T, first: Boolean) -> R ): Map<K, R>
Lenguaje del código: Kotlin (kotlin)

Normalmente se usa cuando fold() y reduce() no cumplen con nuestras necesidades.

Por ejemplo, si quisieras obtener el valor de la suma de salarios por departamentos con aggregate(), podrías usar el parámetro first para inicializar el valor del acumulado y luego ir sumando los valores subsecuentes:

val salariesSumByDepartment = employees .groupingBy(Employee::department) .aggregate { _, sum: Int?, employee, first -> if (first) { employee.salary } else { sum!! + employee.salary } }
Lenguaje del código: Kotlin (kotlin)

¿Ha sido útil esta publicación?