Inspiriert von dieser Antwort, erweiterte ich später einen Blog-Beitrag, in dem ich dies ausführlich durchführte. Ich empfehle, dies zu überprüfen, wenn Sie ein tieferes Verständnis dafür entwickeln möchten, wie Sie über dieses Problem nachdenken können. Ich versuche, es Stück für Stück zu erklären und am Ende einen JSperf-Vergleich zu geben, bei dem die Geschwindigkeitsüberlegungen berücksichtigt werden.
Das heißt, das tl; dr ist folgendes: Um das zu erreichen, was Sie verlangen (Filtern und Zuordnen innerhalb eines Funktionsaufrufs), würden Sie verwendenArray.reduce()
.
Allerdings ist die besser lesbar und (weniger wichtig) in der Regel deutlich schneller 2 Ansatz ist es , nur die Verwendung Filter und Karte miteinander verkettet:
[1,2,3].filter(num => num > 2).map(num => num * 2)
Im Folgenden wird beschrieben, wie dies Array.reduce()
funktioniert und wie Filter und Zuordnung in einer Iteration durchgeführt werden können. Wenn dies zu kurz ist, empfehle ich dringend, den oben verlinkten Blog-Beitrag zu lesen, der ein viel freundlicheres Intro mit klaren Beispielen und Fortschritten darstellt.
Sie geben ein Argument reduzieren, das eine (normalerweise anonyme) Funktion ist.
Diese anonyme Funktion akzeptiert zwei Parameter - einer (wie die an map / filter / forEach übergebenen anonymen Funktionen) ist der Iterat, der bearbeitet werden soll. Es gibt ein weiteres Argument für die übergebene anonyme Funktion, um zu reduzieren, dass diese Funktionen nicht akzeptiert werden. Dies ist der Wert, der zwischen Funktionsaufrufen weitergegeben wird, der häufig als Memo bezeichnet wird .
Beachten Sie, dass Array.filter () nur ein Argument (eine Funktion) akzeptiert, Array.reduce () jedoch auch ein wichtiges (wenn auch optionales) zweites Argument: einen Anfangswert für 'memo', der als dessen an diese anonyme Funktion übergeben wird erstes Argument und kann anschließend mutiert und zwischen Funktionsaufrufen weitergegeben werden. (Wenn es nicht angegeben wird, ist 'memo' im ersten anonymen Funktionsaufruf standardmäßig der erste Iteratee, und das Argument 'iteratee' ist tatsächlich der zweite Wert im Array.)
In unserem Fall übergeben wir zunächst ein leeres Array und wählen dann basierend auf unserer Funktion aus, ob unser Iteratee in unser Array eingefügt werden soll oder nicht - dies ist der Filterprozess.
Schließlich geben wir bei jedem anonymen Funktionsaufruf unser 'Array in Bearbeitung' zurück, und Reduce nimmt diesen Rückgabewert und übergibt ihn als Argument (Memo genannt) an den nächsten Funktionsaufruf.
Auf diese Weise können Filter und Map in einer Iteration ausgeführt werden, wodurch sich die Anzahl der erforderlichen Iterationen halbiert. Bei jeder Iteration wird jedoch nur doppelt so viel Arbeit geleistet, sodass nur Funktionsaufrufe gespeichert werden, die in Javascript nicht so teuer sind .
Eine ausführlichere Erklärung finden Sie in den MDN- Dokumenten (oder in meinem Beitrag, auf den am Anfang dieser Antwort verwiesen wird).
Grundlegendes Beispiel für einen Anruf zum Reduzieren:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
// if condition is our filter
if (iteratee > 1) {
// what happens inside the filter is the map
memo.push(iteratee * 2);
}
// this return value will be passed in as the 'memo' argument
// to the next call of this function, and this function will have
// every element passed into it at some point.
return memo;
}, initialMemo)
console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]
prägnantere Version:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
Beachten Sie, dass der erste Iterat nicht größer als eins war und daher gefiltert wurde. Beachten Sie auch das initialMemo, das nur benannt wurde, um seine Existenz zu verdeutlichen und die Aufmerksamkeit darauf zu lenken. Erneut wird es als 'Memo' an den ersten anonymen Funktionsaufruf übergeben, und dann wird der zurückgegebene Wert der anonymen Funktion als 'Memo'-Argument an die nächste Funktion übergeben.
Ein weiteres Beispiel für den klassischen Anwendungsfall für Memos wäre die Rückgabe der kleinsten oder größten Zahl in einem Array. Beispiel:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.
Ein Beispiel für das Schreiben einer eigenen Reduktionsfunktion (dies hilft mir oft, solche Funktionen zu verstehen):
test_arr = [];
// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
// if we did not pass in a second argument, then our first memo value
// will be whatever is in index zero. (Otherwise, it will
// be that second argument.)
const initialMemoIsIndexZero = arguments.length < 2;
// here we use that logic to set the memo value accordingly.
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
// here we use that same boolean to decide whether the first
// value we pass in as iteratee is either the first or second
// element
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
// memo is either the argument passed in above, or the
// first item in the list. initialIteratee is either the
// first item in the list, or the second item in the list.
memo = reduceFunc(memo, this[i]);
// or, more technically complete, give access to base array
// and index to the reducer as well:
// memo = reduceFunc(memo, this[i], i, this);
}
// after we've compressed the array into a single value,
// we return it.
return memo;
}
Die eigentliche Implementierung ermöglicht beispielsweise den Zugriff auf Dinge wie den Index, aber ich hoffe, dies hilft Ihnen dabei, ein unkompliziertes Gefühl für das Wesentliche zu bekommen.