Dividir uma lista em partes no Kotlin
1. Introdução
Digamos que temos uma matriz como _ [a, b, c, d, e, f] _ e queremos dividir os elementos em grupos separados, como _ _ ou _ _.
*Neste tutorial, conseguiremos isso ao examinar algumas diferenças entre _groupBy, chunked, _ e _windowed _. Do Kotlin.*
*2. Divisão de uma lista em uma lista de pares *
Para nossos exemplos, usaremos duas listas - uma com um número par de elementos e outra com um número ímpar de elementos:
val evenList = listOf(0, "a", 1, "b", 2, "c");
val unevenList = listOf(0, "a", 1, "b", 2, "c", 3);
Claramente, podemos dividir nossa evenList em exatamente três pares. No entanto, nossa unevenList terá um elemento extra.
No restante desta seção, veremos várias implementações para dividir nossas duas listas, incluindo como elas lidam com o elemento extra em unevenList.
====* 2.1 Usando groupBy *
Primeiro, vamos implementar uma solução com groupBy.* Criaremos uma lista com números ascendentes e usaremos groupBy para dividi-los: *
val numberList = listOf(1, 2, 3, 4, 5, 6);
numberList.groupBy { (it + 1)/2 }.values
Isso fornece o resultado desejado:
[[How does it work? Well, _groupBy_ executes the supplied function _(it + 1)/2_ on every element:
* (1 + 1)/2 = 1
* (2 + 1)/2 = 1.5, which is rounded to 1
* (3 + 1)/2 = 2
* (4 + 1)/2 = 2.5, which is rounded to 2
* (5 + 1)/2 = 3
*(6 + 1)/2 = 3.5, which is rounded to 3
Then, _groupBy_ groups the elements in the list that gave the same result.
Now, when we do the same with an uneven list:
[source,java,gutter:,true]
val numberList = listOf (1, 2, 3, 4, 5, 6, 7); numberList.groupBy {(it + 1)/2} .values
We get all the pairs and one extra element: [source,java,gutter:,true]
[[Mas,* se formos um pouco mais longe com alguns números aleatórios: *
val numberList = listOf(1, 3, 8, 20, 23, 30);
numberList.groupBy { (it + 1)/2 }.values
*Obteremos algo completamente indesejado:*
[[The reason is simple; applying the _(it + 1)/2_ function on every element gives: _1, 2, 4, 10, 12, 15_. All the results differ, so no elements are grouped together.
When we use our _evenList_ or _unevenList_, it's even worse — *the code doesn't compile*, as the function cannot be applied to _Strings_.
[[using-groupby-and-withindex]]
==== *2.2. Using _groupBy_ and _withIndex_*
Really, if we want to group an arbitrary list into pairs, *we don't want to modify the __value __by our function, but the _index_:*
[source,java,gutter:,true]
evenList.withIndex () .groupBy {it.index/2} .map {it.value.map {it.value}}
This returns the list of pairs we want: [source,java,gutter:,true]
[[a"], [1, "b"], [2, "c"]]
Furthermore, if we use the _unevenList_, we even get our separate element: [source,java,gutter:,true]
[[a"], [1, "b"], [2, "c"], [3]]
[[using-groupby-with-foldindexed]] ==== *2.3. Using _groupBy_ With _foldIndexed_* We can go a step further than just using _index_ and program a bit more with _foldIndexed_ to save some allocations: [source,java,gutter:,true]
evenList.foldIndexed (ArrayList <ArrayList <Any>> (evenList.size/2)) {index, acc, item → if (index% 2 == 0) {acc.add (ArrayList (2))} acc.last () .add (item) acc}
While a bit more verbose, *the _foldIndexed_ solution simply performs the operation on each element*, whereas the _withIndex_ function first creates an iterator and wraps each element. [[using-chunked]] ==== *2.4. Using _chunked_* *But, we can do this more elegantly with _chunked_. *So, let's apply the method to our _evenList_: [source,java,gutter:,true]
evenList.chunked (2)
The _evenList_ provides us with the pairs we want: [source,java,gutter:,true]
[[a"], [1, "b"], [2, "c"]]
While the _unevenList_ gives us the pairs and the extra element: [source,java,gutter:,true]
[[a"], [1, "b"], [2, "c"], [3]]
[[using-windowed]] ====* 2.5. Using _windowed_* *And _chunked_ works really well, but sometimes we need a bit more control.* For instance, we may need to specify if we want only pairs, or if we want to include the extra element. *The _windowed_ method provides us with a _partialWindows_ Boolean*, which indicates if we want the partial result or not. By default, _partialWindows_ is _false_. So, the following statements produce the same result: [source,java,gutter:,true]
evenList.windowed (2, 2) unevenList.windowed (2, 2, false)
Both return the list without the separate element: [source,java,gutter:,true]
[[a"], [1, "b"], [2, "c"]]
Finally, when we set _partialWindows_ to _true_ to include the partial result: [source,java,gutter:,true]
unevenList.windowed (2, 2, true)
We'll get the list of pairs plus the separate element: [source,java,gutter:,true]
[[a"], [1, "b"], [2, "c"], [3]]
=== *3. Conclusion* Using _groupBy_ is a nice programming exercise, but it can be quite error-prone. Some of the errors can be resolved simply by using an _index_. To optimize the code, we can even use _foldIndexed_. However, this results in even more code. Luckily, the _chunked_ method offers us the same functionality out-of-the-box. Moreover, the _windowed_ method provides additional configuration options. *If possible, it's best to use the _chunked_ method, and if we need additional configuration, we should use the _windowed_ method.* As usual, the full source code is available over on https://github.com/eugenp/tutorials/tree/master/core-kotlin-2[GitHub].