Tablice mają własny zestaw narzędzi
Tablice w JavaScript mają mnóstwo wbudowanych metod. Prawie wszystko, do czego zwykle pisze się pętlę for (przekształcanie wartości, wybieranie części z nich, sumowanie), ma metodę, która robi to w jednej linii, czyta się lepiej i łatwo łączy z innymi.
Zestaw startowy to trzy metody: map, filter i reduce. Opanuj je razem z kilkoma krewnymi, a kod pełen pętli skurczy się do czegoś, co ogarniesz jednym spojrzeniem.
Każda metoda przyjmuje callback i coś zwraca. Żadna z nich nie zmieniła nums: to zasada, którą warto przyswoić jak najwcześniej.
map: przekształć każdy element
map przyjmuje funkcję i wywołuje ją dla każdego elementu, zbierając zwrócone wartości w nowej tablicy o tej samej długości. Używaj jej, gdy chcesz "jeden wynik na każde wejście".
Callback dostaje też indeks jako drugi argument, jeśli go potrzebujesz: arr.map((item, i) => ...). Gdy nie jest potrzebny, po prostu go pomiń.
Częsty błąd: sięganie po map, gdy zwrócona tablica nie jest potrzebna. Jeśli chcesz tylko wypisać każdy element albo zapisać go do bazy danych, to zadanie dla forEach albo zwykłej pętli.
filter: zostaw pasujące elementy
filter wywołuje predykat (funkcję zwracającą true lub false) dla każdego elementu i zostawia te, dla których wynik jest truthy. Nowa tablica ma tę samą długość albo jest krótsza.
map i filter naturalnie łączą się w łańcuch. Czytaj go od lewej do prawej jak potok:
Najpierw filtruj, potem mapuj: dzięki temu map działa tylko na elementach, które przeszły przez filtr.
reduce: sprowadź tablicę do jednej wartości
reduce to najbardziej ogólna z tych trzech metod. Podajesz jej funkcję redukującą (accumulator, item) => newAccumulator i wartość początkową. Metoda przechodzi przez tablicę, przekazuje każdy element do funkcji razem z dotychczasowym akumulatorem i zwraca to, czym akumulator stał się na końcu.
Wynik nie musi być liczbą. Może być obiektem, inną tablicą, stringiem, czymkolwiek, co budujesz:
Zawsze podawaj wartość początkową (drugi argument). Bez niej reduce bierze pierwszy element jako startowy akumulator, co psuje się na pustych tablicach i często i tak nie robi tego, czego chcesz.
reduce jest potężne, ale bywa trudne do czytania, gdy logika się komplikuje. Jeśli twoja funkcja redukująca ma więcej niż kilka linii, zwykła pętla for...of jest często czytelniejsza.
forEach: efekty uboczne, bez wartości zwracanej
forEach to jak map bez zwracanej tablicy. Istnieje po to, żeby zrobić coś z każdym elementem (zalogować go, wywołać API, zaktualizować DOM), gdy nie potrzebujesz nowej kolekcji.
Dwie rzeczy, które warto wiedzieć:
forEachzwracaundefined. Nie da się po nim dołączyć.map().- Nie możesz wcześniej wyjść z
forEachprzezbreak. Jeśli potrzebujesz wcześniejszego wyjścia, użyjfor...ofalbosome/every.
Jeśli łapiesz się na pisaniu arr.forEach(x => results.push(transform(x))), to tak naprawdę jest map.
find i findIndex: tylko jeden, proszę
find zwraca pierwszy element spełniający predykat albo undefined, jeśli nic nie pasuje. findIndex zwraca indeks (albo -1).
find zatrzymuje się na pierwszym dopasowaniu. Nie używaj filter(...)[0]: przeszukuje całą tablicę tylko po to, żeby wyrzucić resztę.
some i every: pytania typu tak/nie
some zwraca true, jeśli przynajmniej jeden element przechodzi test. every zwraca true tylko wtedy, gdy przechodzą go wszystkie.
Obie metody przerywają działanie wcześniej: some zatrzymuje się na pierwszym true, every na pierwszym false. To właściwe narzędzie do pytań "czy którykolwiek…" i "czy wszystkie…".
slice vs splice: kopiowanie vs wycinanie
Te dwie nazwy brzmią podobnie, a robią zupełnie różne rzeczy.
slice(start, end) zwraca płytką kopię części tablicy. Niczego nie modyfikuje. end nie wchodzi do wyniku; pomiń go, żeby iść do końca tablicy.
splice(start, deleteCount, ...items) modyfikuje tablicę w miejscu. Usuwa deleteCount elementów, zaczynając od start, opcjonalnie wstawia nowe i zwraca usunięte elementy.
Sposób na zapamiętanie: slice jest bezpieczne (kopiuje), splice precyzyjnie wycina i edytuje tablicę w miejscu.
Metody mutujące i niemutujące
To rozróżnienie ma znaczenie. Kod, który przypadkiem modyfikuje współdzieloną tablicę, to jeden z bardziej irytujących błędów do wytropienia.
Mutujące (zmieniają oryginał, zwykle zwracają coś innego):
push,pop,shift,unshiftsplice,sort,reversefill,copyWithin
Niemutujące (zwracają nową tablicę lub wartość, oryginał zostaje nietknięty):
map,filter,slice,concatflat,flatMapfind,findIndex,some,every,includes,indexOfreduce,reduceRight
Uważaj szczególnie na sort i reverse: wyglądają niewinnie, a po cichu modyfikują tablicę. Jeśli potrzebujesz posortowanej kopii, najpierw zrób slice:
Nowoczesny JS ma też niemutujące odpowiedniki: toSorted, toReversed, toSpliced i with. Zwracają nowe tablice i zostawiają oryginał w spokoju. Działają we wszystkich aktualnych środowiskach, więc korzystaj z nich, gdy tylko możesz.
flat i flatMap
flat spłaszcza zagnieżdżone tablice o jeden poziom (albo więcej, jeśli podasz argument głębokości). flatMap to map, po którym następuje jednopoziomowe flat: przydaje się, gdy każdy element daje zero lub więcej wyników.
flatMap to czysty sposób na "rozwinięcie" elementów (jedno wejście, wiele wyników) bez dodatkowego kroku flat().
Wszystko razem
Mały, realistyczny przykład. Mając listę zamówień, policz łączny przychód z zakończonych zamówień powyżej 50 $:
Trzy metody, jeden potok, zero ręcznego pilnowania pętli. Każdy krok opisuje, co robi. Można by połączyć oba wywołania filter, ale rozdzielone czytają się dobrze i czasem ułatwiają debugowanie.
Dalej: Map i Set
Tablice dobrze radzą sobie z uporządkowanymi sekwencjami, ale są niewygodne, gdy potrzebujesz szybkiego wyszukiwania po kluczu albo kolekcji unikalnych wartości. JavaScript ma do tego dwie wbudowane struktury danych, Map i Set, i to one są tematem następnej strony.
Najczęściej zadawane pytania
Czym różnią się map, filter i reduce w JavaScript?
map przekształca każdy element i zwraca nową tablicę o tej samej długości. filter zostawia tylko elementy, które spełniają warunek, i zwraca nową (zwykle krótszą) tablicę. reduce przechodzi przez tablicę i sprowadza ją do jednej wartości: sumy, obiektu, innej tablicy, czegokolwiek, co budujesz.
Czym różni się forEach od map?
forEach wywołuje funkcję dla każdego elementu i zwraca undefined: służy do efektów ubocznych. map wywołuje funkcję dla każdego elementu i zwraca nową tablicę z wynikami. Jeśli potrzebujesz przekształconej tablicy, użyj map. Jeśli chcesz tylko coś zrobić z każdym elementem i nie potrzebujesz wyniku, użyj forEach (albo pętli for...of).
Które metody tablic zmieniają oryginalną tablicę?
Mutujące są push, pop, shift, unshift, splice, sort, reverse, fill i copyWithin. Cała reszta, czyli map, filter, slice, concat, flat, flatMap, find, some, every, reduce, zostawia oryginalną tablicę w spokoju i zwraca nową wartość.
Kiedy używać slice, a kiedy splice?
slice(start, end) zwraca płytką kopię fragmentu tablicy i nie rusza oryginału. splice(start, deleteCount, ...items) modyfikuje tablicę: usuwa i/lub wstawia elementy w miejscu i zwraca usunięte. Sposób na zapamiętanie: slice jest bezpieczne, splice precyzyjnie wycina.