Dividi un elenco in parti in Kotlin

1. Introduzione

Supponiamo di avere un array come [a, b, c, d, e, f] e di voler dividere gli elementi in gruppi separati, come [[a, b], [c, d], [e, f ]] o [[a, b, c], [d], [e, f]] .

In questo tutorial, raggiungeremo questo obiettivo esaminando alcune differenze tra groupBy, chunked e windowed di Kotlin .

2. Suddivisione di un elenco in un elenco di coppie

Per i nostri esempi, utilizzeremo due elenchi: uno con un numero pari di elementi e uno con un numero dispari di elementi:

val evenList = listOf(0, "a", 1, "b", 2, "c"); val unevenList = listOf(0, "a", 1, "b", 2, "c", 3);

Chiaramente, possiamo dividere la nostra evenList in esattamente tre coppie. Tuttavia, il nostro unevenList avrà un elemento in più.

Nel resto di questa sezione, vedremo varie implementazioni per dividere i nostri due elenchi, incluso il modo in cui gestiscono l'elemento extra in unevenList .

2.1. Utilizzando groupBy

Innanzitutto, implementiamo una soluzione con g roupBy . Creeremo un elenco con numeri crescenti e useremo groupBy per dividerli:

val numberList = listOf(1, 2, 3, 4, 5, 6); numberList.groupBy { (it + 1) / 2 }.values

Questo dà il risultato desiderato:

[[1, 2], [3, 4], [5, 6]]

Come funziona? Bene, groupBy esegue la funzione fornita (it + 1) / 2 su ogni elemento:

  • (1 + 1) / 2 = 1
  • (2 + 1) / 2 = 1,5, che viene arrotondato a 1
  • (3 + 1) / 2 = 2
  • (4 + 1) / 2 = 2,5, che viene arrotondato a 2
  • (5 + 1) / 2 = 3
  • (6 + 1) / 2 = 3,5, che viene arrotondato a 3

Quindi, groupBy raggruppa gli elementi nell'elenco che hanno dato lo stesso risultato.

Ora, quando facciamo lo stesso con un elenco irregolare:

val numberList = listOf(1, 2, 3, 4, 5, 6, 7); numberList.groupBy { (it + 1) / 2 }.values

Otteniamo tutte le coppie e un elemento in più:

[[1, 2], [3, 4], [5, 6], [7]]

Ma se andiamo un po 'oltre con alcuni numeri casuali:

val numberList = listOf(1, 3, 8, 20, 23, 30); numberList.groupBy { (it + 1) / 2 }.values

Otterremo qualcosa che è completamente indesiderato:

[[1], [3], [8], [20], [23], [30]]

Il motivo è semplice; applicando la funzione (it + 1) / 2 su ogni elemento si ottiene: 1, 2, 4, 10, 12, 15 . Tutti i risultati differiscono, quindi nessun elemento viene raggruppato.

Quando usiamo il nostro evenList o unevenList , è anche peggio: il codice non si compila , poiché la funzione non può essere applicata alle stringhe .

2.2. Utilizzando groupBy e withIndex

In realtà, se vogliamo raggruppare un elenco arbitrario in coppie, non vogliamo modificare il valore dalla nostra funzione, ma l' indice :

evenList.withIndex() .groupBy { it.index / 2 } .map { it.value.map { it.value } }

Questo restituisce l'elenco delle coppie che vogliamo:

[[0, "a"], [1, "b"], [2, "c"]]

Inoltre, se usiamo unevenList , otteniamo anche il nostro elemento separato:

[[0, "a"], [1, "b"], [2, "c"], [3]]

2.3. Usare groupBy con foldIndexed

Possiamo fare un ulteriore passo avanti rispetto al semplice utilizzo di index e programmare un po 'di più con foldIndexed per salvare alcune allocazioni:

evenList.foldIndexed(ArrayList
    
     (evenList.size / 2)) { index, acc, item -> if (index % 2 == 0) { acc.add(ArrayList(2)) } acc.last().add(item) acc }
    

Anche se un po 'più dettagliata, la soluzione foldIndexed esegue semplicemente l'operazione su ogni elemento , mentre la funzione withIndex crea prima un iteratore e avvolge ogni elemento.

2.4. Utilizzando chunked

Ma possiamo farlo in modo più elegante con chunked . Quindi, applichiamo il metodo alla nostra evenList :

evenList.chunked(2)

L'evenList ci fornisce le coppie che vogliamo:

[[0, "a"], [1, "b"], [2, "c"]]

Mentre unevenList ci dà le coppie e l'elemento extra:

[[0, "a"], [1, "b"], [2, "c"], [3]]

2.5. Utilizzando windowed

E il chunked funziona molto bene, ma a volte abbiamo bisogno di un po 'più di controllo.

Ad esempio, potremmo dover specificare se vogliamo solo coppie o se vogliamo includere l'elemento extra. Il metodo windowed ci fornisce un partialWindows Boolean , che indica se vogliamo o meno il risultato parziale.

Per impostazione predefinita, partialWindows è false . Quindi, le seguenti affermazioni producono lo stesso risultato:

evenList.windowed(2, 2) unevenList.windowed(2, 2, false)

Entrambi restituiscono l'elenco senza l'elemento separato:

[[0, "a"], [1, "b"], [2, "c"]]

Infine, quando impostiamo partialWindows su true per includere il risultato parziale:

unevenList.windowed(2, 2, true)

Otterremo l'elenco delle coppie più l'elemento separato:

[[0, "a"], [1, "b"], [2, "c"], [3]]

3. Conclusione

Usare groupBy è un bel esercizio di programmazione, ma può essere abbastanza soggetto a errori. Alcuni degli errori possono essere risolti semplicemente utilizzando un indice .

Per ottimizzare il codice, possiamo anche usare foldIndexed . Tuttavia, questo si traduce in ancora più codice. Fortunatamente, il metodo chunked ci offre la stessa funzionalità out-of-the-box.

Inoltre, il metodo in finestra fornisce ulteriori opzioni di configurazione. Se possibile, è meglio usare il metodo chunked e se abbiamo bisogno di una configurazione aggiuntiva, dovremmo usare il metodo a finestre .

Come al solito, il codice sorgente completo è disponibile su GitHub.