2012-03-27 2 views
314

Ich habe folgende für Schleife, und wenn ich splice() verwenden, um ein Element zu entfernen, bekomme ich dann, dass "Sekunden" undefiniert ist. Ich könnte prüfen, ob es undefiniert ist, aber ich denke, dass es einen eleganteren Weg gibt, dies zu tun. Der Wunsch ist, einfach einen Gegenstand zu löschen und weiterzumachen.Looping durch Array und Entfernen von Elementen, ohne für Schleife zu brechen

for (i = 0, len = Auction.auctions.length; i < len; i++) { 
    auction = Auction.auctions[i]; 
    Auction.auctions[i]['seconds'] --; 
    if (auction.seconds < 0) { 
     Auction.auctions.splice(i, 1); 
    }   
} 
+6

Zusätzlich zu Iteration rückwärts und Länge anpassen, können Sie auch die gewünschten Mitglieder in ein neues Array einfügen. – RobG

Antwort

630

Das Array wird neu indiziert, wenn Sie eine .splice() tun, was bedeutet, dass Sie einen Index überspringen, wenn einer entfernt wird, und Ihre zwischengespeicherte .length veraltet ist.

es zu beheben, dann würden Sie müssen entweder i nach einem .splice() verringern, oder einfach in umgekehrter Reihenfolge durchlaufen ...

var i = Auction.auctions.length 
while (i--) { 
    ... 
    if (...) { 
     Auction.auctions.splice(i, 1); 
    } 
} 

Auf diese Weise die Neuindexierung wirkt sich nicht auf das nächste Element in dem Iteration, da die Indexierung nur die Elemente vom aktuellen Punkt bis zum Ende des Arrays betrifft und das nächste Element in der Iteration niedriger als der aktuelle Punkt ist.

+1

Sieht für mich die beste Lösung aus. Und der schnellste übrigens. –

+1

Schön! Warum ist das ein Community-Wiki? – 0xc0de

+0

Interessanterweise funktioniert diese Lösung in ihrer derzeitigen Form vermutlich nicht, wenn das Array ein Array von Funktionen ist, die Sie durchlaufen und in der Reihenfolge aufrufen möchten, in der sie zum Array hinzugefügt wurden, zum Beispiel? – ComethTheNerd

34

die Länge jedes Mal durch die Schleife neu berechnen, statt nur am Anfang, z.B .:

for (i = 0; i < Auction.auctions.length; i++) { 
     auction = Auction.auctions[i]; 
     Auction.auctions[i]['seconds'] --; 
     if (auction.seconds < 0) { 
      Auction.auctions.splice(i, 1); 
      i--; //decrement 
     } 
} 

diese Weise können Sie nicht die Grenzen überschreiten.

BEARBEITEN: Hinzufügen eines Dekrements in der if-Anweisung.

+7

I thnk ist es einfacher, rückwärts durch das Array zu gehen, dann müssen Sie die Länge überhaupt nicht anpassen. – RobG

+4

Ich habe auch übersehen, die Array-Länge nach dem Spleißen in einer Schleife neu zu berechnen. Lustig, wie etwas so Einfaches uns dazu bringen kann, uns für einen Moment die Köpfe zu kratzen. –

18

Obwohl Ihre Frage über das Löschen von Elementen aus das Array auf iteriert wird und nicht über das Entfernen von Elementen (zusätzlich zu einer anderen Verarbeitung) effizient, ich denke, man sollte es in ähnlicher Situation überdenken. Die algorithmische Komplexität dieses Ansatzes ist O(n^2), da die Spleißfunktion und die for-Schleife beide über das Array iterieren (die Spleißfunktion verschiebt im schlimmsten Fall alle Elemente des Arrays). Stattdessen können Sie einfach die erforderlichen Elemente auf das neue Array schieben und dann dieses Array der gewünschten Variablen zuweisen (die gerade erst iteriert wurde).

var newArray = []; 
for (var i = 0, len = Auction.auctions.length; i < len; i++) { 
    auction = Auction.auctions[i]; 
    auction.seconds--; 
    if (!auction.seconds < 0) { 
     newArray.push(auction); 
    } 
} 
Auction.auctions = newArray; 

Oder nutzen Sie die Array-Funktionen Pfeil und Funktion alles auf eine Zeile zu passen:

Auction.auctions = Auction.auctions.filter(auction => --auction.seconds >=0); 
+1

Ich denke Ihre Methode ist die sicherste, da sie ein neues Array erstellt, müssen wir uns keine Gedanken über den for() Index zu "brechen". Mit neueren Browsern, obwohl wir stattdessen die 'Array.filter()' Funktion verwenden können: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter –

+0

beachten Sie, dass die Filterversion reduziert hier nicht die Sekunden bei jeder Auktion wie die for-Schleife! – nus

+0

Hat irgendjemand einen Benchmark bei der Verwendung von 'filter' vs. rückwärts iterieren +' splice'? – qxz

13
Auction.auction = Auction.auctions.filter(function(el) { 
    return --el["seconds"] > 0; 
}); 
+0

Dies ist eine coole Methode, ähnlich wie 0xc0de oben. Einziges Problem, es wurde nur in IE9 + implementiert ... also nicht abwärtskompatibel. –

+5

@AlexisWilke: Zum Glück sind wir jetzt in der Zukunft und können auf den Gräbern des IE8 und seiner Vorgänger tanzen. –

78

Das ist ein ziemlich häufiges Problem. Die Lösung ist in einer Schleife zurück:

for (var i = Auction.auctions.length - 1; i >= 0; i--) { 
    Auction.auctions[i].seconds--; 
    if (Auction.auctions[i].seconds < 0) { 
     Auction.auctions.splice(i, 1); 
    } 
} 

Es spielt keine Rolle, wenn man sie vom Ende, weil die Indizes sind knallen wird, wie Sie rückwärts gehen erhalten bleiben.

0

Versuchen ein Array in newArray weiterzuleiten, wenn Looping:

var auctions = Auction.auctions; 
var auctionIndex; 
var auction; 
var newAuctions = []; 

for (
    auctionIndex = 0; 
    auctionIndex < Auction.auctions.length; 
    auctionIndex++) { 

    auction = auctions[auctionIndex]; 

    if (auction.seconds >= 0) { 
    newAuctions.push(
     auction); 
    }  
} 

Auction.auctions = newAuctions; 
8

Hier ist ein weiteres Beispiel für den richtigen Gebrauch der Spleiß ist. In diesem Beispiel wird "Attribut" aus "Array" entfernt.

for (var i = array.length; i--;) { 
    if (array[i] === 'attribute') { 
     array.splice(i, 1); 
    } 
} 
+0

sollte nicht das array.length-1 sein? – Izzy

+0

Ich denke nicht, weil 'i -' den Index reduziert. Wenn du 'array machst.length-1' dann wird das letzte Element von 'array' übersprungen –

+1

Ah ich sehe, aber ich muss ein wenig argumentieren, dass es nicht so lesbar ist. Auch wenn ich kein Anfänger bin, habe ich auf den ersten Blick auch nicht gesehen, dass man innerhalb des Checkblocks von for loop "i" dekrementiert. 'für (var i = array.length-1; i> = 0; i--)' ist viel sauberer (und es erzeugt denselben Effekt) – Izzy

8

Eine weitere einfache Lösung, wenn ein Array-Elemente zu verdauen:

while(Auction.auctions.length){ 
    // From first to last... 
    var auction = Auction.auctions.shift(); 
    // From last to first... 
    var auction = Auction.auctions.pop(); 

    // Do stuff with auction 
} 
1
for (i = 0, len = Auction.auctions.length; i < len; i++) { 
    auction = Auction.auctions[i]; 
    Auction.auctions[i]['seconds'] --; 
    if (auction.seconds < 0) { 
     Auction.auctions.splice(i, 1); 
     i--; 
     len--; 
    } 
} 
+3

Eine ** gute Antwort ** wird immer eine Erklärung haben, was gemacht wurde und warum es so gemacht wurde, nicht nur für das OP, sondern für zukünftige Besucher von SO. –

0

können Sie nur durch aussehen und shift()

+2

Fügen Sie ein Beispiel mit dieser Methode hinzu. – Ivan

1

verwenden Wenn Sie E sind mit ES6 + - warum nicht einfach verwenden Array.filtermethode?

Auction.auctions = Auction.auctions.filter((auction) => { 
    auction['seconds'] --; 
    return (auction.seconds > 0) 
}) 

Beachten Sie, dass nur während der Filter Iteration das Array-Element Modifizieren von Objekten arbeitet und wird für Array primitiver Werte nicht funktionieren.

Verwandte Themen