Diviser une liste en parties dans Kotlin

1. Introduction

Disons que nous avons un tableau comme [a, b, c, d, e, f] et que nous voulons diviser les éléments en groupes séparés, comme [[a, b], [c, d], [e, f ]] ou [[a, b, c], [d], [e, f]] .

Dans ce didacticiel, nous y parviendrons en examinant certaines différences entre groupBy de Kotlin , fragmenté et fenêtré .

2. Diviser une liste en une liste de paires

Pour nos exemples, nous utiliserons deux listes - une avec un nombre pair d'éléments et une avec un nombre impair d'éléments:

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

Clairement, nous pouvons diviser notre evenList en exactement trois paires. Cependant, notre liste inégale aura un élément supplémentaire.

Dans le reste de cette section, nous verrons diverses implémentations pour diviser nos deux listes, y compris comment elles traitent l'élément supplémentaire dans unevenList .

2.1. Utilisation de groupBy

Tout d'abord, implémentons une solution avec g roupBy . Nous allons créer une liste avec des nombres croissants et utiliser groupBy pour les diviser:

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

Cela donne le résultat souhaité:

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

Comment ça marche? Eh bien, groupBy exécute la fonction fournie (it + 1) / 2 sur chaque élément:

  • (1 + 1) / 2 = 1
  • (2 + 1) / 2 = 1,5, arrondi à 1
  • (3 + 1) / 2 = 2
  • (4 + 1) / 2 = 2,5, arrondi à 2
  • (5 + 1) / 2 = 3
  • (6 + 1) / 2 = 3,5, arrondi à 3

Ensuite, groupBy regroupe les éléments de la liste qui ont donné le même résultat.

Maintenant, quand nous faisons la même chose avec une liste inégale:

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

Nous obtenons toutes les paires et un élément supplémentaire:

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

Mais, si nous allons un peu plus loin avec des nombres aléatoires:

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

Nous obtiendrons quelque chose qui est complètement indésirable:

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

La raison est simple; appliquer la fonction (it + 1) / 2 sur chaque élément donne: 1, 2, 4, 10, 12, 15 . Tous les résultats diffèrent, donc aucun élément n'est regroupé.

Lorsque nous utilisons notre evenList ou unevenList , c'est encore pire: le code ne se compile pas , car la fonction ne peut pas être appliquée aux chaînes .

2.2. Utilisation de groupBy et withIndex

Vraiment, si nous voulons regrouper une liste arbitraire en paires, nous ne voulons pas modifier la valeur par notre fonction, mais l' index :

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

Cela renvoie la liste des paires que nous voulons:

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

De plus, si nous utilisons la liste inégale , nous obtenons même notre élément séparé:

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

2.3. Utilisation de groupBy avec foldIndexed

Nous pouvons aller plus loin que simplement utiliser index et programmer un peu plus avec foldIndexed pour enregistrer certaines allocations:

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

Bien qu'un peu plus verbeuse, la solution foldIndexed effectue simplement l'opération sur chaque élément , tandis que la fonction withIndex crée d'abord un itérateur et encapsule chaque élément.

2.4. Utilisation de morceaux

Mais, nous pouvons le faire plus élégamment avec chunked . Alors, appliquons la méthode à notre evenList :

evenList.chunked(2)

La liste evenList nous fournit les paires que nous voulons:

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

Alors que la liste inégale nous donne les paires et l'élément supplémentaire:

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

2.5. Utilisation fenêtrée

Et chunked fonctionne très bien, mais parfois nous avons besoin d'un peu plus de contrôle.

Par exemple, nous pouvons avoir besoin de spécifier si nous ne voulons que des paires, ou si nous voulons inclure l'élément supplémentaire. La méthode fenêtrée nous fournit un partialWindows Boolean , qui indique si nous voulons le résultat partiel ou non.

Par défaut, partialWindows est false . Ainsi, les instructions suivantes produisent le même résultat:

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

Les deux renvoient la liste sans l'élément séparé:

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

Enfin, lorsque nous définissons partialWindows sur true pour inclure le résultat partiel:

unevenList.windowed(2, 2, true)

Nous obtiendrons la liste des paires plus l'élément séparé:

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

3. Conclusion

Utiliser groupBy est un bon exercice de programmation, mais il peut être assez sujet aux erreurs. Certaines des erreurs peuvent être résolues simplement en utilisant un index .

Pour optimiser le code, nous pouvons même utiliser foldIndexed . Cependant, cela entraîne encore plus de code. Heureusement, la méthode fragmentée nous offre la même fonctionnalité prête à l' emploi .

De plus, la méthode fenêtrée fournit des options de configuration supplémentaires. Si possible, il est préférable d'utiliser la méthode chunked , et si nous avons besoin d'une configuration supplémentaire, nous devrions utiliser la méthode fenêtrée .

Comme d'habitude, le code source complet est disponible à l'adresse over sur GitHub.