656 Stimmen

Schleife durch Array und Entfernen von Elementen, ohne die for-Schleife zu unterbrechen

Ich habe die folgende for-Schleife, und wenn ich verwenden splice() um ein Element zu entfernen, erhalte ich die Meldung, dass "Sekunden" nicht definiert ist. Ich könnte überprüfen, ob es undefiniert ist, aber ich denke, es gibt wahrscheinlich einen eleganteren Weg, dies zu tun. Der Wunsch ist es, einfach ein Element zu löschen und weiterzumachen.

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);
    }           
}

1087voto

Das Array wird neu indiziert, wenn Sie eine .splice() was bedeutet, dass ein Index übersprungen wird, wenn einer entfernt wird, und dass Ihre zwischengespeicherten .length ist obsolet.

Um das zu beheben, müssen Sie entweder den Wert von i nach einer .splice() oder einfach rückwärts iterieren...

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

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

265voto

frattaro Punkte 2912

Dies ist ein ziemlich häufiges Problem. Die Lösung ist eine Rückwärtsschleife:

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, ob Sie sie am Ende abziehen, denn die Indizes bleiben erhalten, wenn Sie rückwärts gehen.

60voto

Marc Punkte 10755

Berechnen Sie die Länge jedes Mal neu, wenn Sie die Schleife durchlaufen, anstatt 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
      }
}

Auf diese Weise werden Sie die Grenzen nicht überschreiten.

EDIT: Ein Dekrement in der if-Anweisung hinzugefügt.

58voto

0xc0de Punkte 7588

Obwohl sich Ihre Frage auf das Löschen von Elementen aus das Array, über das iteriert wird und nicht um das effiziente Entfernen von Elementen (zusätzlich zu einigen anderen Verarbeitungen) geht, denke ich, dass man dies in einer ähnlichen Situation noch einmal überdenken sollte.

Die algorithmische Komplexität dieses Ansatzes ist O(n^2) da sowohl die Spleißfunktion als auch die for-Schleife über das Array iterieren (die Spleißfunktion verschiebt im schlimmsten Fall alle Elemente des Arrays). Stattdessen können Sie einfach die erforderlichen Elemente in das neue Array verschieben und dieses Array dann der gewünschten Variablen zuweisen (über die gerade 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;

Seit ES2015 können wir Array.prototype.filter um alles in einer Zeile unterzubringen:

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

28voto

Aesthete Punkte 17984
Auction.auctions = Auction.auctions.filter(function(el) {
  return --el["seconds"] > 0;
});

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X