Elemente mit Array.map in JavaScript entfernen

87

Ich möchte ein Array von Elementen mithilfe der map()Funktion filtern . Hier ist ein Code-Snippet:

var filteredItems = items.map(function(item)
{
    if( ...some condition... )
    {
        return item;
    }
});

Das Problem ist, dass herausgefilterte Elemente immer noch Speicherplatz im Array belegen und ich sie vollständig löschen möchte.

Irgendeine Idee?

EDIT: Danke, ich habe vergessen filter(), was ich wollte, ist eigentlich ein filter()dann ein map().

EDIT2: Vielen Dank für den Hinweis map()und filter()sind nicht in allen Browsern implementiert, obwohl mein spezifischer Code nicht für die Ausführung in einem Browser vorgesehen war.

Vincent Robert
quelle
Können Sie erläutern, warum 2 Iterationen am schlechtesten sind als 1? Ich meine, 2 * O (n) entspricht für mich O (2 * n) ...
Vincent Robert

Antworten:

103

Sie sollten die filterMethode anstelle der Zuordnung verwenden, es sei denn, Sie möchten die Elemente im Array zusätzlich zur Filterung mutieren.

z.B.

var filteredItems = items.filter(function(item)
{
    return ...some condition...;
});

[Bearbeiten: Natürlich können Sie immer sourceArray.filter(...).map(...)sowohl filtern als auch mutieren]

olliej
quelle
3
mapmutiert nicht
Danke
14
Aber du kannst mutieren map.
Crazywako
Vorsicht: Wenn JS eine Referenz übergibt, wenn Sie etwas mit map mutieren, ändert sich das Objekt, aber wenn MDN steht, gibt maps das mutierte Array zurück.
AlexOtano
1
Bei der Frage wurde nicht gefragt, wie gefiltert werden soll, sondern wie auf der Karte gelöscht werden soll
Dazzle
1
@alexOtano Nein, map mutiert nicht und gibt kein mutiertes Array zurück. Es wird ein neues Array zurückgegeben. zBx=[1,2,3];y = x.map(z => z*2);console.log(x,y);
Kyle Baker
39

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.

Kyle Baker
quelle
2
brillant! Ich wollte schon seit Jahren so etwas machen. Beschlossen, ein schönes und wegweisendes und wow, natürliches Javascript herauszufinden!
jemiloii
Ein weiterer Vorteil von reduceist, dass dem Rückruf im Gegensatz zu filter+ ein mapIndexargument übergeben werden kann, das der Index des ursprünglichen Arrays und nicht der des gefilterten ist.
Congusbongus
@KyleBaker Der Link zu Ihrem Blog-Beitrag führt zu einer Seite, die nicht gefunden wurde. Können Sie bitte den Link aktualisieren? Vielen Dank!
Tim Philip
10

Das macht die Karte nicht. Sie wollen wirklich Array.filter . Oder wenn Sie die Elemente wirklich aus der ursprünglichen Liste entfernen möchten, müssen Sie dies unbedingt mit einer for-Schleife tun.

Patrick
quelle
6

Array- Filter-Methode

var arr = [1, 2, 3]

// ES5 syntax
arr = arr.filter(function(item){ return item != 3 })

// ES2015 syntax
arr = arr.filter(item => item != 3)

console.log( arr )

vsync
quelle
1
Sie können auch tunvar arr = [1,2,"xxx", "yyy"]; arr = arr.filter(function(e){ return e!="xxx" }) console.log(arr)
Jack Blank
Sie sind 4 Jahre später zurückgekommen, um großen Text hinzuzufügen? minus eins
Danke
@ user633183 Wen beziehen Sie? Welcher "riesige Text"? Ihr Kommentar ist unklar. Sind Sie sicher, dass Sie den richtigen Ort kommentieren ...?
vsync
2

Sie müssen jedoch beachten, dass das Array.filternicht in allen Browsern unterstützt wird, daher müssen Sie Prototypen erstellen:

//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license

if (!Array.prototype.filter)
{
    Array.prototype.filter = function(fun /*, thisp*/)
    {
        var len = this.length;

        if (typeof fun != "function")
            throw new TypeError();

        var res = new Array();
        var thisp = arguments[1];

        for (var i = 0; i < len; i++)
        {
            if (i in this)
            {
                var val = this[i]; // in case fun mutates this

                if (fun.call(thisp, val, i, this))
                   res.push(val);
            }
        }

        return res;
    };
}

Auf diese Weise können Sie jede Methode prototypisieren, die Sie benötigen.

ggasp
quelle
1
Wenn Sie diese Methode wirklich polyfillieren möchten, verwenden Sie bitte eine geeignete Polyfill oder besser noch eine Bibliothek wie Modernizr . Andernfalls werden Sie wahrscheinlich auf verwirrende Fehler mit obskuren Browsern stoßen, die Sie erst erkennen, wenn sie zu lange in Produktion sind.
Kyle Baker
0

Die folgende Anweisung bereinigt das Objekt mithilfe der Kartenfunktion.

var arraytoclean = [{v:65, toberemoved:"gronf"}, {v:12, toberemoved:null}, {v:4}];
arraytoclean.map((x,i)=>x.toberemoved=undefined);
console.dir(arraytoclean);
Nicolas
quelle
0

Ich habe gerade eine Array-Schnittmenge geschrieben, die auch Duplikate korrekt behandelt

https://gist.github.com/gkucmierz/8ee04544fa842411f7553ef66ac2fcf0

// array intersection that correctly handles also duplicates

const intersection = (a1, a2) => {
  const cnt = new Map();
  a2.map(el => cnt[el] = el in cnt ? cnt[el] + 1 : 1);
  return a1.filter(el => el in cnt && 0 < cnt[el]--);
};

const l = console.log;
l(intersection('1234'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('12344'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('1234'.split``, '33456'.split``)); // [ '3', '4' ]
l(intersection('12334'.split``, '33456'.split``)); // [ '3', '3', '4' ]

gkucmierz
quelle
0

Zuerst können Sie die Karte verwenden und mit der Verkettung können Sie den Filter verwenden

state.map(item => {
            if(item.id === action.item.id){   
                    return {
                        id : action.item.id,
                        name : item.name,
                        price: item.price,
                        quantity : item.quantity-1
                    }

            }else{
                return item;
            }
        }).filter(item => {
            if(item.quantity <= 0){
                return false;
            }else{
                return true;
            }
        });
Rishab
quelle