En este tutorial verás el uso de la función chunked
en Kotlin, para dividir una colección en listas más pequeñas de un tamaño dado. Encontrarás varios ejemplos para demostrar el uso de su sintaxis normal y su forma sobrecargada con una función de transformación.
Función chunked()
Usa a la función de extensión chunked()
para trocear una colección en partes. El tamaño de cada parte lo provees como parámetro y el resultado será una lista con las listas de partes.
// chunked en iterables
fun <T> Iterable<T>.chunked(size: Int): List<List<T>>
// chunked en strings
fun CharSequence.chunked(size: Int): List<String>
La primera parte se construye a partir del primer elemento de la colección. Luego se evalúa el valor de n
con respecto al tamaño de la colección, si soporta otra parte, entonces la construye y así sucesivamente. La última parte variará su tamaño de las demás si separación no fue armónica.
Dividir Una Lista Con chunked()
Tomemos como ejemplo una lista de pares que representan el nombre y edad de los estudiantes de un curso. Se requiere dividir el grupo de alumnos en grupos de 3 personas:
fun main() {
val studentsAndAges = listOf(
"Ana" to 10, "Enzo" to 9, "Eric" to 11,
"Eva" to 9, "Hugo" to 10, "Iván" to 11,
"Juan" to 11, "Lara" to 11, "Leo" to 9,
"Luz" to 10
)
val groupsOf3 = studentsAndAges.chunked(3)
groupsOf3.forEach { println(it) }
}
Salida:
[(Ana, 10), (Enzo, 9), (Eric, 11)]
[(Eva, 9), (Hugo, 10), (Iván, 11)]
[(Juan, 11), (Lara, 11), (Leo, 9)]
[(Luz, 10)]
Función chunked() Con Transformación
Existe una variación sobrecargada para chunked()
que recibe como segundo parámetro una función lambda de transformación. Esta se usa para producir resultados que reduzcan cada parte a un valor esperado para satisfacer tu necesidad.
fun <T, R> Iterable<T>.chunked(
size: Int,
transform: (List<T>) -> R
): List<R>
Separar Y Mapear Lista Con chunked()
Por ejemplo, ahora supongamos que se debe elegir como líder a los estudiantes mayores de cada grupo en la situación pasada. Luego se deben imprimir los nombres de los líderes.
fun main() {
val studentsAndAges = listOf(
"Ana" to 10, "Enzo" to 9, "Eric" to 11,
"Eva" to 9, "Hugo" to 10, "Iván" to 11,
"Juan" to 11, "Lara" to 11, "Leo" to 9,
"Luz" to 10
)
val leaderNames = studentsAndAges.chunked(3) { group ->
val oldestStudent = group.maxByOrNull { student -> student.second }
val oldestStudentName = oldestStudent?.first
oldestStudentName
}
println(leaderNames)
}
Salida:
[Eric, Iván, Juan, Luz]
La solución consiste en pasar como segundo parámetro una lambda de múltiples líneas (recuerda que se puede omitir el paréntesis si es el último argumento) a chunked()
. La primer sentencia obtiene el estudiante mayor a través de maxByOrNull()
y la edad del estudiante.
Luego se consigue el nombre de oldestStudentName
a través del primer elemento del par, dejándolo como valor de consumido. De esta forma se termina partiendo y mapeando a la lista en una sola operación.
Romper En Partes Un String
Usemos como referencia un escenario donde se obtiene un contenido de texto con caracteres numéricos consecutivos. Estos representan fechas obtenidas a través de un bot que las recolecta de páginas web.
El objetivo es romper en partes el String
de las fechas, convertirlas a enteros, ordenarlas en forma ascendente y luego imprimirlas:
fun main() {
val textContent = "201220142009202120012004"
val yearIntSize = 4
val scrapedYears = textContent
.chunked(yearIntSize) { it.toString().toInt() }
.sorted()
println(scrapedYears)
}
Salida:
[2001, 2004, 2009, 2012, 2014, 2021]
Como ves, la solución consiste en separar a textContent
con chunked()
, pasando el tamaño 4
y agregando una conversión a entero en la transformación. Luego encadenamos una operación sorted()
y al final imprimimos el resultado final de scrapedYears
.