Menu

Metody tablic w JavaScript: map, filter, reduce i inne

Metody tablic, które zastępują większość pętli for: map, filter, reduce, find, some i every. Zobacz, które zmieniają tablicę, a które zwracają nową.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

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ć:

  • forEach zwraca undefined. Nie da się po nim dołączyć .map().
  • Nie możesz wcześniej wyjść z forEach przez break. Jeśli potrzebujesz wcześniejszego wyjścia, użyj for...of albo some/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, unshift
  • splice, sort, reverse
  • fill, copyWithin

Niemutujące (zwracają nową tablicę lub wartość, oryginał zostaje nietknięty):

  • map, filter, slice, concat
  • flat, flatMap
  • find, findIndex, some, every, includes, indexOf
  • reduce, 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.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ