Zainspirowany napisaniem tej odpowiedzi, później rozwinąłem i napisałem post na blogu, szczegółowo omawiając ten temat. Polecam to sprawdzić, jeśli chcesz głębiej zrozumieć, jak myśleć o tym problemie - staram się to wyjaśnić kawałek po kawałku, a na końcu podam porównanie z JSperfem, omawiając kwestie dotyczące szybkości.
To powiedziawszy, tl; dr jest takie: Aby osiągnąć to, o co prosisz (filtrowanie i mapowanie w ramach jednego wywołania funkcji), użyłbyśArray.reduce()
.
Jednak bardziej czytelnym i (co mniej ważne) zwykle znacznie szybszym 2 podejściem jest po prostu użycie filtra i mapy połączonych ze sobą:
[1,2,3].filter(num => num > 2).map(num => num * 2)
Poniżej znajduje się opis tego, jak Array.reduce()
działa i jak można go użyć do wykonania filtrowania i mapowania w jednej iteracji. Ponownie, jeśli jest to zbyt skondensowane, gorąco polecam przeczytanie powyższego wpisu na blogu, który jest znacznie bardziej przyjaznym wprowadzeniem z jasnymi przykładami i postępem.
Podajesz redukuj argument, który jest (zwykle anonimową) funkcją.
Ta funkcja anonimowa przyjmuje dwa parametry - jeden (podobnie jak funkcje anonimowe przekazane do map / filter / forEach) jest iteracją, na której ma być wykonywana operacja. Jest jeszcze jeden argument przemawiający za przekazaniem funkcji anonimowej w celu zmniejszenia jednak faktu, że te funkcje nie akceptują i jest to wartość, która zostanie przekazana pomiędzy wywołaniami funkcji, często nazywana memo .
Zauważ, że chociaż Array.filter () przyjmuje tylko jeden argument (funkcję), Array.reduce () przyjmuje również ważny (choć opcjonalny) drugi argument: wartość początkową dla 'memo', która zostanie przekazana do tej anonimowej funkcji jako jej pierwszy argument, a następnie może być mutowany i przekazywany między wywołaniami funkcji. (Jeśli nie zostanie podany, to „memo” w pierwszym wywołaniu funkcji anonimowej będzie domyślnie pierwszą iteracją, a argument „iteratee” będzie w rzeczywistości drugą wartością w tablicy)
W naszym przypadku przekażemy pustą tablicę, aby rozpocząć, a następnie wybierzemy, czy wstawić naszą iterację do naszej tablicy, czy nie, na podstawie naszej funkcji - to jest proces filtrowania.
Na koniec zwrócimy naszą „tablicę w toku” przy każdym wywołaniu funkcji anonimowej, a reduktor pobierze tę zwróconą wartość i przekaże ją jako argument (zwany memo) do następnego wywołania funkcji.
Pozwala to filtrować i mapować w jednej iteracji, zmniejszając liczbę wymaganych iteracji o połowę - ale po prostu wykonując dwa razy więcej pracy w każdej iteracji, więc nic nie jest tak naprawdę zapisywane poza wywołaniami funkcji, które nie są tak drogie w javascript .
Pełniejsze wyjaśnienie można znaleźć w dokumentach MDN (lub w moim poście, do którego odwołuje się na początku tej odpowiedzi).
Podstawowy przykład połączenia Reduce:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
if (iteratee > 1) {
memo.push(iteratee * 2);
}
return memo;
}, initialMemo)
console.log(array)
bardziej zwięzła wersja:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
Zauważ, że pierwsza iteracja nie była większa niż jeden, więc została odfiltrowana. Zwróć także uwagę na początkoweMemo, nazwane tylko po to, aby wyjaśnić jego istnienie i zwrócić na nie uwagę. Ponownie jest przekazywana jako „memo” do pierwszego wywołania funkcji anonimowej, a następnie zwracana wartość funkcji anonimowej jest przekazywana jako argument „memo” do następnej funkcji.
Innym przykładem klasycznego przypadku użycia dla memo byłoby zwrócenie najmniejszej lub największej liczby w tablicy. Przykład:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
Przykład, jak napisać własną funkcję redukuj (uważam, że często pomaga to zrozumieć takie funkcje):
test_arr = [];
test_arr.my_reducer = function(reduceFunc, initialMemo) {
const initialMemoIsIndexZero = arguments.length < 2;
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
memo = reduceFunc(memo, this[i]);
}
return memo;
}
Rzeczywista implementacja umożliwia na przykład dostęp do takich elementów, jak indeks, ale mam nadzieję, że pomoże ci to w nieskomplikowanym odczuciu jego istoty.