Разделете списък на части в Kotlin

1. Въведение

Да предположим, че имаме масив като [a, b, c, d, e, f] и искаме да разделим елементите на отделни групи, като [[a, b], [c, d], [e, f ]] или [[a, b, c], [d], [e, f]] .

В този урок ще постигнем това, докато изследваме някои разлики между групата на Kotlin Byy, chunked и windowed .

2. Разделяне на списък в списък с двойки

За нашите примери ще използваме два списъка - един с четен брой елементи и един с нечетен брой елементи:

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

Ясно е, че можем да разделим нашия evenList на точно три двойки. Нашият unevenList обаче ще има един допълнителен елемент.

В останалата част на този раздел ще видим различни изпълнения за разделяне на двата ни списъка, включително как се справят с допълнителния елемент в unevenList .

2.1. Използване на groupBy

Първо, нека приложим решение с g roupBy . Ще създадем списък с възходящи числа и ще използваме groupBy, за да ги разделим:

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

Това дава желания резултат:

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

Как работи? Е, groupBy изпълнява предоставената функция (it + 1) / 2 за всеки елемент:

  • (1 + 1) / 2 = 1
  • (2 + 1) / 2 = 1,5, което се закръглява до 1
  • (3 + 1) / 2 = 2
  • (4 + 1) / 2 = 2,5, което се закръглява до 2
  • (5 + 1) / 2 = 3
  • (6 + 1) / 2 = 3,5, което се закръглява до 3

След това groupBy групира елементите в списъка, които дават същия резултат.

Сега, когато правим същото с неравен списък:

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

Получаваме всички двойки и един допълнителен елемент:

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

Но ако отидем малко по-далеч с някои случайни числа:

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

Ще получим нещо, което е напълно нежелано:

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

Причината е проста; прилагането на функцията (it + 1) / 2 върху всеки елемент дава: 1, 2, 4, 10, 12, 15 . Всички резултати се различават, така че никакви елементи не са групирани заедно.

Когато използваме нашия evenList или unevenList , това е още по-лошо - кодът не се компилира , тъй като функцията не може да бъде приложена към Strings .

2.2. Използване на groupBy и withIndex

Наистина, ако искаме да групираме произволен списък по двойки, не искаме да променяме стойността чрез нашата функция, а индекса :

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

Това връща списъка с двойки, които искаме:

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

Освен това, ако използваме unevenList , ние дори получаваме нашия отделен елемент:

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

2.3. Използване на groupBy С foldIndexed

Можем да отидем стъпка по-далеч от простото използване на index и да програмираме малко повече с foldIndexed, за да запазим някои разпределения:

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

Макар и малко по-подробно, решението foldIndexed просто изпълнява операцията върху всеки елемент , докато функцията withIndex първо създава итератор и обвива всеки елемент.

2.4. Използване на парченца

Но можем да направим това по-елегантно с парченца . И така, нека приложим метода към нашия evenList :

evenList.chunked(2)

В evenList ни дава двойките, които искаме:

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

Докато unevenList ни дава двойките и допълнителния елемент:

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

2.5. Използване на прозорец

И нарязаните работи наистина добре, но понякога се нуждаем от малко повече контрол.

Например, може да се наложи да посочим дали искаме само двойки или искаме да включим допълнителния елемент. Методът с прозорци ни предоставя частичен булев бутон на Windows , който показва дали искаме частичния резултат или не.

По подразбиране partWindows е невярно . И така, следните твърдения дават същия резултат:

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

И двете връщат списъка без отделния елемент:

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

И накрая, когато зададем partWindows на true, за да включва частичния резултат:

unevenList.windowed(2, 2, true)

Ще получим списъка с двойки плюс отделния елемент:

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

3. Заключение

Използването на groupBy е приятно упражнение за програмиране, но може да бъде доста податливо на грешки. Някои от грешките могат да бъдат разрешени просто с помощта на индекс .

За да оптимизираме кода, можем дори да използваме foldIndexed . Това обаче води до още повече код. За щастие, раздробеният метод ни предлага същата функционалност извън комплекта.

Освен това прозоречният метод предоставя допълнителни опции за конфигуриране. Ако е възможно, най-добре е да използвате раздробения метод и ако се нуждаем от допълнителна конфигурация, трябва да използваме прозоречния метод.

Както обикновено, пълният изходен код е достъпен в GitHub.