Wykonujemy ćwiczenie, jakim jest napisanie funkcji zip w języku JavaScript. Trzy różne, proste rozwiązania – tak długo, jak piszemy funkcję zipującą tylko dwie tablice, jest naprawdę prosto. Na koniec trudne wyzwanie, które albo pokochamy albo znienawidzimy – napisać zip dla dowolnej ilości tablic.

W języku Python funkcja zip bierze dwie (lub więcej) tablice o jednakowej długości i prezentuje listę zawierającą krotki z parami element pierwszej + element drugiej tablicy.

W JS nie mamy krotek (takie listy niemutowalne), więc użyjemy list. Zadanie jest proste – wchodzą dwie listy o jednakowej długości, wychodzi jedna z zagnieżdżonymi listami zawierającymi pary elementów z każdej listy w kolejności.

Sposób 1 – klasyczna pętla for

Do przejścia po indeksach listy możemy użyć klasycznej pętli for. Obie tablice są takiej samej długości, więc nie robi nam różnicy, którą wybierzemy:

<script>
        function zip(arr1, arr2) {
        let zipped = [];
        for(let idx = 0; idx <arr1.length; idx++) {
            zipped.push([arr1[idx], arr2[idx]]);
        }
        return zipped;
        }
        console.log(zip([1,2,3], ['a', 'b', 'c']));
        //[ [1, 'a'], [2, 'b'], [3, 'c']]
  </script>

Przechodzimy po indeksach i do zwracanej tablicy zipped dodajemy tablicę z odpowiednią parą.

Sposób 2 – for…in

Do przechodzenia po indeksach możemy użyć też pętli for…in (łatwo zapamiętać, od INdeks, przechodzi po indeksach):

<script>
        function zip(arr1, arr2) {
        let zipped = [];
        for(let idx in arr1) {
            zipped.push([arr1[idx], arr2[idx]]);
        }
        return zipped;
        }
        console.log(zip([1,2,3], ['a', 'b', 'c']));
        //[ [1, 'a'], [2, 'b'], [3, 'c']]
    </script>

Niewiele się to różni od poprzedniego podejścia, działa jak zip.

Sposób 3 – map na indeksach

Stwórzmy tablicę zawierającą tyle pustych elementów, ile ma pierwsza tablica (obie mają jednakową długość):

function zip(arr1, arr2) {
   let arr = Array(arr1.length);
 }

Teraz wypełnijmy ją zerami:

function zip(arr1, arr2) {
   let idx = 0;
   let arr = Array(arr1.length).fill(idx);
  }

Fajnie, tylko nam przydałaby się tablica z liczbami od 0 wzwyż, tak samo jak indeksy.

Szczerze mówiąc, chciałem to zrobić w ten sposób:

function zip(arr1, arr2) {
   let idx = 0;
   let arr = Array(arr1.length).fill(idx++);
    console.log(arr);
}
zip([1,2,3], [1,2,3]);
//[0,0,0]

Byłem przekonany, że dostanę tablicę od 0 do końca, w tym wypadku do 2 (ostatni indeks arr1).

Tak jednak nie jest. Cóż, rozwiązanie będzie mniej czytelne niż to sobie wymarzyłem, ale nie lubię się poddawać.

Mamy tablicę pustych elementów. Taka tablica zawiera puste elementy, ale ma swoją długość.

function zip(arr1, arr2) {
        let idx = 0;
        let arr = Array(arr1.length);
        while(idx < arr.length)
            arr[idx] = idx++;
        console.log(arr);
        }
        zip([1,2,3], [1,2,3]);
        //[0,1,2]

Jeżeli ma długość, możemy przejść po niej w pętli while. Dobra, nieważne – mamy tablicę indeksów. Teraz funkcja map:

function zip(arr1, arr2) {
        let idx = 0;
        let arr = Array(arr1.length);
        while(idx < arr.length)
            arr[idx] = idx++;
        return arr.map((el) => [arr1[el], arr2[el]]);  
        }
        console.log(zip([1,2,3], [1,2,3]));
        
        //[[1,1], [2,2], [3,3]]

Szczerze mówiąc, ten przykład był w zamyśle ładny. Później się okazało, że fill nie potrafi wypełnić tablicy indeksami.

Możemy sobie to zatem uprościć:

function zip(arr1, arr2) {
  return arr1.map((_, idx) => [arr1[idx], arr2[idx]]);
  }

 console.log(zip([1,2,3], [1,2,3]));
 //[[1,1], [2,2], [3,3]]

Wyzwanie – zip dla wielu tablic

To była dotychczas zabawa. Teraz chcemy, aby można było robić zip na wielu tablicach.

Wyzwanie nie jest łatwe – inaczej nie byłoby wyzwaniem.

Rozwiązanie:

function zip(arrays) {
    return arrays[0].map(function(_, i) {
        return arrays.map(function(array) {
            return array[i];
        });
    });
}

// Example usage:
var array1 = [1, 2, 3];
var array2 = ['a', 'b', 'c'];
var array3 = [true, false, true];

var zipped = zip([array1, array2, array3]);
console.log(zipped);

Możemy to sobie na spokojnie przeanalizować. Wynik wygląda następująco:

[
    [1, 'a', true],
    [2, 'b', false],
    [3, 'c', true]
]

Czyli przekazujemy tablicę z tablicami do zip. Z pierwszej (wszystkie są jednakowej długości) pobieramy sobie indeksy poprzez map.

Funkcja wrzucona do map zwraca kolejne map na naszej tablicy tablic, gdzie zwracamy pierwszy (potem drugi, trzeci) element naszych tablic.

Podchwytliwe, trudne – tak. Inaczej nie byłoby wyzwaniem. Możemy to sobie przeanalizować raz jeszcze – albo znaleźć inny sposób.