Powiedzmy, że chcę sumować a.x
dla każdego elementu arr
.
arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN
Mam powody sądzić, że topór jest w pewnym momencie niezdefiniowany.
Następujące działa dobrze
arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7
Co robię źle w pierwszym przykładzie?
arr.reduce(function(a,b){return a + b})
drugi przykład.Odpowiedzi:
Po pierwszej iteracji zwracasz liczbę, a następnie próbujesz uzyskać jej właściwość
x
, aby dodać ją do następnego obiektu, który obejmujeundefined
matematykę iundefined
wynikiNaN
.spróbuj zwrócić obiekt zawierający
x
właściwość z sumą x właściwości parametrów:Wyjaśnienie dodano z komentarzy:
Zwracana wartość każdej iteracji
[].reduce
używana jakoa
zmienna w następnej iteracji.Iteracji 1:
a = {x:1}
,b = {x:2}
,{x: 3}
przypisanya
w iteracji 2Iteracja 2:
a = {x:3}
,b = {x:4}
.Problem z twoim przykładem polega na tym, że zwracasz literał liczbowy.
Iteracja 1:
a = {x:1}
,b = {x:2}
,// returns 3
jaka
w następnej iteracjiIteracja 2:
a = 3
,b = {x:2}
powracaNaN
Szereg dosłowny
3
nie (zazwyczaj) mają właściwość o nazwiex
więcundefined
iundefined + b.x
powracaNaN
iNaN + <anything>
jest zawszeNaN
Wyjaśnienie : Wolę moją metodę niż drugą najwyższą odpowiedź w tym wątku, ponieważ nie zgadzam się z pomysłem, że przekazanie opcjonalnego parametru zmniejszania za pomocą magicznej liczby w celu uzyskania liczby pierwotnej jest czystsze. Może to skutkować zapisaniem mniejszej liczby wierszy, ale imo jest mniej czytelne.
źródło
Lepszym sposobem na osiągnięcie tego jest podanie wartości początkowej:
Przy pierwszym wywołaniu funkcji anonimowej zostaje wywołana za pomocą
(0, {x: 1})
i zwraca0 + 1 = 1
. Następnym razem zostaje wywołany(1, {x: 2})
i wraca1 + 2 = 3
. Następnie jest wywoływany(3, {x: 4})
, w końcu wraca7
.źródło
reduce
.TL; DR, ustaw wartość początkową
Wykorzystanie destrukcji
arr.reduce( ( sum, { x } ) => sum + x , 0)
Bez destrukcji
arr.reduce( ( sum , cur ) => sum + cur.x , 0)
Z maszynopisu
arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)
Spróbujmy metody destrukcji:
Kluczem do tego jest ustawienie wartości początkowej. Zwracana wartość staje się pierwszym parametrem następnej iteracji.
Technika zastosowana w górnej odpowiedzi nie jest idiomatyczna
Przyjęta odpowiedź proponuje NIE przekazywanie wartości „opcjonalnej”. Jest to błędne, ponieważ idiomatyczny sposób polega na tym, że zawsze dołączany jest drugi parametr . Czemu? Trzy powody:
1. Niebezpieczne - nieprzekazanie wartości początkowej jest niebezpieczne i może powodować skutki uboczne i mutacje, jeśli funkcja oddzwaniania jest nieostrożna.
Ujrzeć
Gdybyśmy jednak zrobili to w ten sposób, z wartością początkową:
Dla potrzeb rekordu, chyba że masz zamiar zmutować oryginalny obiekt, ustaw pierwszy parametr
Object.assign
na pusty obiekt. Tak:Object.assign({}, a, b, c)
.2 - Lepsze wnioskowanie o typach - podczas korzystania z narzędzia takiego jak Typescript lub edytora, takiego jak VS Code, zyskujesz na informowaniu kompilatora o inicjałach i może on wychwytywać błędy, jeśli popełnisz błąd. Jeśli nie ustawisz wartości początkowej, w wielu sytuacjach może nie być w stanie zgadnąć, co może spowodować przerażające błędy w czasie wykonywania.
3 - Respect the Functors - JavaScript świeci najlepiej, gdy jego wewnętrzne funkcjonalne dziecko jest uruchomione. W świecie funkcjonalnym istnieje standard określający sposób „składania” lub
reduce
macierzy. Kiedy spasujesz lub zastosujesz katamorfizm do tablicy, bierzesz wartości tej tablicy, aby skonstruować nowy typ. Musisz przekazać wynikowy typ - powinieneś to zrobić, nawet jeśli końcowym typem są wartości z tablicy, innej tablicy lub dowolnego innego typu.Pomyślmy o tym w inny sposób. W JavaScript funkcje mogą być przekazywane jak dane, tak działają wywołania zwrotne, co jest wynikiem następującego kodu?
[1,2,3].reduce(callback)
Czy zwróci liczbę? Obiekt? To czyni to jaśniejszym
[1,2,3].reduce(callback,0)
Przeczytaj więcej na temat specyfikacji programowania funkcjonalnego tutaj: https://github.com/fantasyland/fantasy-land#foldable
Trochę więcej tła
reduce
Sposób dwa parametry,callback
Funkcja przyjmuje następujące parametryW pierwszej iteracji
Jeśli
initialItem
podano,reduce
funkcja przekazujeinitialItem
jakoaccumulator
i pierwszy element tablicy jakoitemInArray
.Jeśli nie
initialItem
jest podana, funkcja przekazuje pierwszy element w tablicy jako drugi element w tablicy, co może być mylące.reduce
initialItem
itemInArray
Uczę i zalecam zawsze ustawianie początkowej wartości redukcji.
Możesz sprawdzić dokumentację na:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Mam nadzieję że to pomoże!
źródło
Inni odpowiedzieli na to pytanie, ale myślałem, że rzuciłbym inne podejście. Zamiast przejść bezpośrednio do sumowania siekiery, możesz połączyć mapę (od siekiery do x) i zmniejszyć (aby dodać x):
Wprawdzie prawdopodobnie będzie nieco wolniej, ale pomyślałem, że warto wspomnieć o tym jako opcji.
źródło
Aby sformalizować to, co zostało wskazane, reduktor jest katamorfizmem, który przyjmuje dwa argumenty, które mogą być tego samego typu przez przypadek, i zwraca typ, który pasuje do pierwszego argumentu.
Oznacza to, że korpus reduktora musi polegać na konwersji
currentValue
i bieżącej wartościaccumulator
na wartość nowegoaccumulator
.Działa to w prosty sposób podczas dodawania, ponieważ zarówno akumulator, jak i wartości elementu są tego samego typu (ale służą innym celom).
To po prostu działa, ponieważ wszystkie są liczbami.
Teraz podajemy wartość początkową agregatorowi. Wartością początkową powinien być typ, jakiego oczekuje się od agregatora (typ, który ma się pojawić jako wartość końcowa), w zdecydowanej większości przypadków. Chociaż nie musisz tego robić (i nie powinno tak być), należy o tym pamiętać.
Kiedy już to wiesz, możesz napisać znaczące redukcje dla innych problemów w relacji n: 1.
Usuwanie powtarzających się słów:
Podając liczbę wszystkich znalezionych słów:
Zmniejszenie tablicy tablic do pojedynczej płaskiej tablicy:
Za każdym razem, gdy chcesz przejść od szeregu rzeczy do pojedynczej wartości, która nie pasuje do 1: 1, warto rozważyć redukcję.
W rzeczywistości mapę i filtr można wdrożyć jako redukcje:
Mam nadzieję, że zapewnia to dodatkowy kontekst użycia
reduce
.Jedynym dodatkiem do tego, do czego jeszcze się nie włamałem, jest oczekiwanie, że typy wejściowe i wyjściowe mają być dynamiczne, ponieważ elementy tablicy są funkcjami:
źródło
Dla pierwszej iteracji „a” będzie pierwszym obiektem w tablicy, stąd ax + bx zwróci 1 + 2, tj. 3. Teraz w następnej iteracji zwrócone 3 jest przypisane do a, a więc a jest liczbą teraz n nazywającą siekierę da NaN.
Prostym rozwiązaniem jest najpierw mapowanie liczb w tablicy, a następnie ich zmniejszenie, jak poniżej:
tutaj
arr.map(a=>a.x)
zapewni tablicę liczb [1,2,4]. Teraz.reduce(function(a,b){return a+b})
wystarczy użyć tych liczb bez żadnego hasselInnym prostym rozwiązaniem jest podanie początkowej sumy jako zero poprzez przypisanie 0 do „a” jak poniżej:
źródło
Na każdym etapie redukcji nie zwracasz nowego
{x:???}
obiektu. Więc albo musisz zrobić:lub musisz to zrobić
źródło
W pierwszym kroku będzie działać dobrze, ponieważ wartość
a
będzie wynosić 1, a wartośćb
będzie wynosić 2, ale jako 2 + 1 zostanie zwrócona, aw następnym kroku wartośćb
będzie wartością zwracaną,from step 1 i.e 3
a zatemb.x
będzie niezdefiniowana. .i undefined + anyNumber będzie NaN i dlatego otrzymujesz ten wynik.Zamiast tego możesz spróbować, podając wartość początkową jako zero, tj
źródło
Jeśli masz złożony obiekt z dużą ilością danych, taki jak tablica obiektów, możesz rozwiązać problem krok po kroku.
Na przykład:
Najpierw powinieneś zmapować tablicę na nową tablicę, która Cię interesuje, w tym przykładzie może to być nowa tablica wartości.
Ta funkcja zwrotna zwróci nową tablicę zawierającą tylko wartości z oryginalnej tablicy i zapisze ją w wartości const. Teraz twoje wartości const są tablicą podobną do tej:
A teraz jesteś gotowy, aby wykonać redukcję:
Jak widać, metoda zmniejszająca wykonuje funkcję wywołania zwrotnego wiele razy. Za każdym razem pobiera bieżącą wartość elementu z tablicy i sumuje z akumulatorem. Aby właściwie to zsumować, musisz ustawić wartość początkową akumulatora jako drugi argument metody redukcji.
Teraz masz nową stałą sumę o wartości 30.
źródło
zredukuj iteracje funkcji po kolekcji
przetłumaczyć na:
Aby zrozumieć wejścia funkcji „zmniejsz”, sugeruję zajrzeć do kodu źródłowego tej funkcji. Biblioteka Lodash ma funkcję zmniejszania, która działa dokładnie tak samo jak funkcja „zmniejsz” w ES6.
Oto link: zmniejsz kod źródłowy
źródło
aby zwrócić sumę wszystkich
x
rekwizytów:źródło
Możesz użyć mapy i ograniczyć funkcje
źródło
Funkcja redukcji tablic przyjmuje trzy parametry, tj. Wartość początkową (domyślnie jest to 0), akumulator i wartość prądu. Domyślnie wartość initialValue będzie wynosić „0”. który jest pobierany przez akumulator
Zobaczmy to w kodzie.
Teraz ten sam przykład z wartością początkową:
To samo dotyczy również tablic obiektowych (aktualne pytanie dotyczące przepływu stosu):
JEDNA RZECZ, KTÓRA NADUŻYĆ W UMYSŁU, domyślnie jest to wartość początkowa 0 i można podać wszystko, co mam na myśli {}, [] i liczbę
źródło
źródło
nie powinieneś używać siekiery jako akumulatora, zamiast tego możesz zrobić tak: `arr = [{x: 1}, {x: 2}, {x: 4}]
arr.reduce (funkcja (a, b) {a + bx}, 0) `
źródło