[JS] Jak manipulować tablicami i nie tylko za pomocą JavaScript
Do napisania tego wpisu zainspirowali mnie znajomi, którzy do tej pory zajmowali się głównie PHP lub innymi językami programowania, a o JSie wiedzieli tyle, że jest i jak użyć jQuery
W tym artykule skupię się na pokazaniu jak można operować na tablicach oraz na obiektach bez potrzeby ładowania takich bibliotek jak jQuery czy lodash.
Iterowanie po tablicy za pomocą forEach
Patrząc na kod znajomych to bardzo często widziałem, że korzystali iterowania po tablicy za pomocą pętli for
. Nie mówię, że to źle, ale można w takich sytuacjach wykorzystać funkcję forEach()
do tego celu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | var data = [12,'21',56,'04']; /////////////// // stara wersja for (var index = 0; index < data.length; index++) { console.log(data[i], i); } // wyniki: // 12, 0 // 21, 1 // 56, 2 // 04, 3 ////////////// // nowa wersja data.forEach(function (element, index) { console.log(element, index); }); ////////// // wyniki: // // 12, 0 // 21, 1 // 56, 2 // 04, 3 |
Jak widać, zapis z wykorzystaniem funkcji forEach()
jest przyjemniejszy w czytaniu a wydajność w większości przypadków jest prawie taka sama. Dodatkowo, nie trzeba się martwić o samodzielne tworzenie indeksów i manipulowanie nimi.
Parsowanie danych za pomocą funkcji map()
Bardzo często spotkałem się również z wykorzystaniem for
tylko po to, aby utworzyć nową tablicę z danymi pochodzącymi z innej tablicy tylko odpowiednio przetworzonymi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | var data = [{ id: 24, name: 'Piotr', lastName: 'Nalepa' }, { id: 25, name: 'Jan', lastName: 'Kowalski' }, { id: 26, name: 'Anna', lastName: 'Nowak' }]; var parsedData = []; /////////////// // stara wersja for (var index = 0; index < data.length; index++) { parsedData[index] = data[index].name + ' ' + data[index].lastName; } for (var index2 = 0; index2 < parsedData.length; index2++) { console.log(parsedData[index2]); } ////////// // wyniki: // // Piotr Nalepa // Jan Kowalski // Anna Nowak ////////////// // nowa wersja data.map(function (element) { return [element.name, ' ', element.lastName].join(''); }).forEach(function (element) { console.log(element); }); ////////// // wyniki: // // Piotr Nalepa // Jan Kowalski // Anna Nowak |
Jak widać na przykładzie powyżej, w starej wersji potrzebowaliśmy tworzyć nową tablicę aby przechować w niej przetworzone dane (po wykonaniu pierwszej pętli), a potem musieliśmy utworzyć kolejny index aby każdy element z tablicy wywołać przy pomocy funkcji console.log()
.
W nowej wersji to wygląda o wiele prościej, raz że za pomocą funkcji map()
przetworzyliśmy dane to jeszcze mogliśmy przekazać wynik przetwarzania do następnej funkcji o nazwie forEach()
i tam wywołać każdy element z przetworzoną wersją danych w funkcji console.log()
. To wszystko ma jeszcze tą zaletę, że zarówno map()
jak i forEach()
nie modyfikują pierwotnej wersji danych ze zmiennej data
tylko tworzą w pamięci nową tablicę z danymi, którą można przypisać do dowolnej zmiennej.
Filtrowanie tablicy lub wyszukiwanie elementów w tablicy za pomocą filter()
Jeśli mamy już jakieś dane w postaci tablicy, to nie raz pojawia się potrzeba aby te dane jakoś przefiltrować pod jakimś kątem i zwrócić nową tablicę.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | var data = [{ sum: 1, name: 'Item §', id: 'i-21' }, { sum: 12345, name: 'Item 1', id: 'i-32' }, { sum: 99, name: 'Item 2', id: 'i-45' }, { sum: 123, name: 'Item 3', id: 'i-19' }, { sum: 8, name: 'Item 4', id: 'i-12' }]; data.filter(function (item) { return item.sum > 100; }).forEach(function (item) { console.log(item.name); }); ////////// // wyniki: // // Item 1 // Item 3 |
Dane z tablicy znajdującej się w zmiennej data
przefiltrowaliśmy pod kątem sumy większej niż 100, a następnie je wyświetliliśmy w konsoli przeglądarki.
Sumowanie danych tablicy za pomocą reduce()
Jedną z mniej znanych możliwości udostępnionych przez tablice w JS jest sumowanie wartości elementów znajdujących się w tablicy. Aby zsumować elementy należy wykorzystać funkcję reduce()
w następujący sposób:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | var data = [{ sum: 1, name: 'Item §', id: 'i-21' }, { sum: 12345, name: 'Item 1', id: 'i-32' }, { sum: 99, name: 'Item 2', id: 'i-45' }, { sum: 123, name: 'Item 3', id: 'i-19' }, { sum: 8, name: 'Item 4', id: 'i-12' }]; var sum = data.reduce(function (total, next) { return total + next.sum; }, 0); console.log(sum); ////////// // wynik: // // 12576 |
Jak widać, mając tablicę obiektów jesteśmy w łatwy sposób zsumować sobie wartości obiektów z odpowiedniej własności obiektu (w przypadku powyżej - z własności sum
). Warto pamiętać o tym, że pierwszy parametr funkcji przekazywanej do wywołania to jest zawsze suma elementów już policzonych, a drugi parametr to jest następny element.
Gdybyśmy do funkcji reduce()
dodali nie przekazali drugiego parametru (tj. 0
) to nasze suma naszych elementów wyszłaby NaN z tego względu, że przy pierwszej iteracji dodawalibyśmy liczbę do obiektu.
Sprawdzenie czy występuje przynajmniej jeden element o określonych cechach za pomocą funkcji some()
W przypadku gdybyśmy chcieli sprawdzić czy jakikolwiek element został usunięty (wg posiadanych przez nas danych) i na tej podstawie wykonać jakąś dalszą część kodu to możemy wykorzystać funkcję some()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | var data = [{ isRemoved: false, name: 'Item §', id: 'i-21' }, { isRemoved: false, name: 'Item 1', id: 'i-32' }, { isRemoved: false, name: 'Item 2', id: 'i-45' }, { isRemoved: true, name: 'Item 3', id: 'i-19' }, { isRemoved: false, name: 'Item 4', id: 'i-12' }]; var anyRemoved = data.some(function (item) { return item.isRemoved; }); if (anyRemoved) { console.log('Jakiś element został usunięty'); } ////////// // wynik: // // Jakiś element został usunięty |
Funkcja some()
zawsze zwraca albo prawdę albo fałsz. W przypadku powyżej, zwróciła prawdę i na tej podstawie wywnioskowaliśmy, że jakiś element ma status usuniętego.
Sprawdzenie czy wszystkie elementy tablicy spełniają wymagane warunki za pomocą funkcji every()
W porównaniu do funkcji some()
funkcja every()
sprawdza czy wszystkie elementy w tablicy spełniają wymagania:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | var data = [{ isRemoved: false, name: 'Item §', id: 'i-21' }, { isRemoved: false, name: 'Item 1', id: 'i-32' }, { isRemoved: false, name: 'Item 2', id: 'i-45' }, { isRemoved: true, name: 'Item 3', id: 'i-19' }, { isRemoved: false, name: 'Item 4', id: 'i-12' }]; var allRemoved = data.every(function (item) { return item.isRemoved; }); if (allRemoved) { console.log('Wszystkie elementy mają stan usuniętego'); } ////////// // wynik: // // [brak] |
W przypadku powyżej, nie uruchomi się funkcja console.log()
z tego względu, że w tablicy znajduje się tylko jeden element ze stanem usuniętego. Gdyby wszystkie elementy w tablicy miały dla własności isRemoved
wartość true
, to dopiero wtedy funkcja every()
zwróciłaby true
.
Sortowanie wyników tablicy za pomocą funkcji sort()
Elementy w tablicach można sortować na wiele różnych sposobów rosnąco bądź malejąco. W poniższym przykładzie pokażę sposób jak posortować tablicę obiektów wg ID elementu w sposób rosnący:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | var data = [{ isRemoved: false, name: 'Item §', id: '21' }, { isRemoved: false, name: 'Item 1', id: '32' }, { isRemoved: false, name: 'Item 2', id: '45' }, { isRemoved: true, name: 'Item 3', id: '19' }, { isRemoved: false, name: 'Item 4', id: '12' }]; data.sort(function (a, b) { return parseInt(a.id, 10) - parseInt(b.id, 10); }); console.log(data); ////////// // wynik: // // zostanie wyświetlona tablica z elementami posortowanymi wg kolejności rosnącej // na podstawie własności `id` |
Jak pewne zauważyłeś/aś, w funkcji sortującej korzystam z funkcji parseInt([liczb], 10)
. Jest to dobra praktyka parsowania elementy typu String to elementu typu Number przed przystąpieniem do odejmowania jednej wartości od drugiej. Gdybyśmy zamienili a
z b
to uzyskalibyśmy tablicę posortowaną malejąco. Co ciekawe, gdy korzystamy z funkcji sort()
to nie musimy przypisywać wyniku sortowania do nowej zmiennej ponieważ pierwotna wersja tablicy (pochodząca ze zmiennej data
) zostanie posortowana.
Pobranie pierwszych 10 elementów z tablicy
Czasami bywa tak, że mamy zbyt wiele danych. Potrzebujemy tylko ich część, a reszta jest zbędna i nie będzie wykorzystywana. Jeśli potrzebujemy, to możemy wykonać jakieś filtrowanie lub sortowanie. Na koniec jednak chcielibyśmy pobrać pierwsze 10 wyników z naszej tabeli z danymi. Są dwa sposoby na to. Pierwszy:
1 2 3 4 5 6 7 8 9 10 | var data = [1,2,123,44,85,67,17,8,9,19,234,36,98,66,50,32]; data.length = 10; console.log(data); ////////// // wynik: // // zostanie wyświetlona tablica z 10 elementami |
Drugi sposób jest nieco inny. Gdybyśmy jednak potrzebowali danych do późniejszego wykorzystania to możemy je zachować na później bez ich tracenia:
1 2 3 4 5 6 7 8 9 | var data = [1,2,123,44,85,67,17,8,9,19,234,36,98,66,50,32]; var partialData = data.slice(0, 2); console.log(partialData, data); ////////// // wynik: // // zostanie wyświetlona tablica z 2-ma elementami oraz tablica z 16-ma elementami |
Ten drugi sposób, może być stosowany w dzieleniu danych z tablicy na strony, co może się przydać w tworzeniu różnego rodzaju stronicowanych tabel lub widoków.
Usuwanie konkretnego elementu z tablicy za pomocą funkcji splice()
W przypadku gdybyśmy chcieli usunąć jakiś element z tablicy, to możemy to zrobić za pomocą poniższego kodu:
1 2 3 4 5 6 7 8 9 10 | var data = [1,2,123,44]; data.splice(data.indexOf(123), 1); console.log(data); ////////// // wynik: // // [1,2,44] |
Zastosowaliśmy tutaj dwie funkcje: indexOf()
oraz splice()
. Za pomocą pierwszej funkcji znaleźliśmy index elementu który chcielibyśmy usunąć, a za pomocą drugiej funkcji usunęliśmy element z tablicy.
Co ciekawe, za pomocą tej samej metody możemy zastąpić usunięty element innym: data.splice(data.indexOf(123), 1, 99);
Łączenie tablic za pomocą funkcji concat()
W przypadku, gdy mamy dwie tablice i chcielibyśmy je złączyć w jedną tablicę to wystarczy użyć funkcji concat()
:
1 2 3 4 5 6 7 8 9 10 11 12 | var data1 = ['1', 2]; var data2 = ['a', 'b']; var total; total = data1.concat(data2); console.log(total); ////////// // wynik: // // ['1', 2, 'a', 'b'] |
Za pomocą funkcji concat()
możemy łączyć wiele tablic w jedną. Wystarczy podać kolejne tablice jako kolejne parametry we funkcji concat()
.
Odwracanie wartości w tablicy za pomocą funkcji reverse()
Czasami trzeba odwrócić wartości w tablicy. Jest do tego osobna funkcja:
1 2 3 4 5 6 7 8 9 10 | var data = [2,5,9,3]; data.reverse(); console.log(data); ////////// // wynik: // // [3, 9, 5, 2] |
Osobiście, niezbyt często korzystam z tej funkcji, ale czasem bywa przydatna.
Iterowanie po obiektach (lub innymi słowy, po tablicy asocjacyjnej)
W świecie PHP bardzo ma się do czynienia z tablicami asocjacyjnymi, które w świecie JavaScript są obiektami. Nie ma oczywistego sposobu na iterowanie po kluczach w obiekcie, tak by brał pod uwagę tylko własne klucze, a nie również dziedziczone (tego tematu nie będę omawiał tutaj). Sposobem z którego najczęściej korzystam to:
1 2 3 4 5 6 7 8 9 10 11 12 | var data = { name: 'Piotr', id: 21, details: { job: 'developer', city: 'Katowice' } }; Object.keys(data).forEach(function (key) { console.log(data[key]); }); |
Najpierw listuję wszystkie klucze obiektu do tablicy, a następnie iteruję po nich i w ten sposób nie muszę znać kluczy, aby dobrać się do wartości ukrytych pod kluczami.
Podsumowanie
Tym artykułem chciałem wskazać kilka sposobów na operowanie na tablicach osobom, które nie są na bieżąco z informacjami o JavaScripcie lub przychodzą z innych języków programowania, a w ich głowach panuje przesąd, że JavaScript niewiele się zmienił od 2007 roku 😉
Mam nadzieję, że ta dawka wiedzy pozwoli Ci na sprawne manipulowanie danymi w swoim kodzie i nie będzie trzeba korzystać cały czas z pętli for
do wszystkiego.