webentwicklung-frage-antwort-db.com.de

Durchlaufen Sie das Array und entfernen Sie Elemente, ohne die Schleife zu durchbrechen

Ich habe die folgende for-Schleife, und wenn ich splice() verwende, um ein Element zu entfernen, dann bekomme ich, dass 'Sekunden' undefiniert ist. Ich könnte überprüfen, ob es undefiniert ist, aber ich glaube, es gibt wahrscheinlich einen eleganteren Weg, dies zu tun. Der Wunsch ist, 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);
    }           
}
382
dzm

Das Array wird neu indiziert, wenn Sie eine .splice() ausführen. Das heißt, Sie überspringen einen Index, wenn einer entfernt wird, und Ihr zwischengespeicherter .length ist veraltet.

Um das Problem zu beheben, müssen Sie entweder i nach einer .splice() dekrementieren oder einfach in umgekehrter Reihenfolge durchlaufen ...

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 die Indizierung 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.

746
user1106925

Dies ist ein ziemlich häufiges Problem. Die Lösung besteht darin, rückwärts zu schleifen:

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 Sie sie vom Ende wegwerfen, weil die Indizes beim Rückwärtsgehen erhalten bleiben.

110
frattaro

Berechnen Sie die Länge jedes Mal neu durch die Schleife und nicht erst am Anfang, z.

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: In der if-Anweisung wurde ein Dekrement hinzugefügt.

38
Marc

Ihre Frage betrifft zwar das Löschen von Elementen aus des Arrays, das nach iteriert wird, und nicht das effiziente Entfernen von Elementen (zusätzlich zu einer anderen Verarbeitung). Ich denke, man sollte es in einer ähnlichen Situation überdenken.

Die algorithmische Komplexität dieses Ansatzes ist O(n^2) als Splice-Funktion, und die for-Schleife durchläuft beide Felder über das Array (Splice-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 (die gerade durchlaufen 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 verwenden, um alles in eine Zeile zu bringen:

Auction.auctions = Auction.auctions.filter(auction => --auction.seconds >= 0);
23
0xc0de
Auction.auction = Auction.auctions.filter(function(el) {
  return --el["seconds"] > 0;
});
16
Aesthete

Hier ist ein weiteres Beispiel für die richtige Verwendung von Spleißen. In diesem Beispiel werden 'Attribute' von 'Array' entfernt.

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

Eine weitere einfache Lösung, um ein Array-Element einmal 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
}
8
Pablo

Wenn Sie mit ES6 + arbeiten, verwenden Sie die Methode Array.filter. 

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

Beachten Sie, dass das Ändern des Array-Elements während der Filteriteration nur für Objekte und nicht für Array von Grundwerten funktioniert.

4
Rubinsh

An jede Person, die diese sehr grundlegende Frage mit Code beantwortet hat, der splice () in einer Schleife hat, die die Laufzeit O (n) hat2), oder wer hat eine solche Antwort in den sieben Jahren, seitdem diese Frage gestellt wurde, positiv bewertet: Sie sollten sich schämen .

Hier ist eine einfache lineare Zeitlösung für dieses einfache lineare Zeitproblem.

Wenn ich dieses Snippet mit n = 1 Million ausführe, dauert jeder Aufruf von filterInPlace () 0,013 bis 0,016 Sekunden. Eine quadratische Lösung (z. B. die akzeptierte Antwort) würde eine Million Mal so viel benötigen.

// Remove from array every item such that !condition(item).
function filterInPlace(array, condition) {
   var iOut = 0;
   for (var i = 0; i < array.length; i++)
     if (condition(array[i]))
       array[iOut++] = array[i];
   array.length = iOut;
}

// Try it out.  A quadratic solution would take a very long time.
var n = 1*1000*1000;
console.log("constructing array...");
var Auction = {auctions: []};
for (var i = 0; i < n; ++i) {
  Auction.auctions.Push({seconds:1});
  Auction.auctions.Push({seconds:2});
  Auction.auctions.Push({seconds:0});
}
console.log("array length should be "+(3*n)+": ", Auction.auctions.length)
filterInPlace(Auction.auctions, function(auction) {return --auction.seconds >= 0; })
console.log("array length should be "+(2*n)+": ", Auction.auctions.length)
filterInPlace(Auction.auctions, function(auction) {return --auction.seconds >= 0; })
console.log("array length should be "+n+": ", Auction.auctions.length)
filterInPlace(Auction.auctions, function(auction) {return --auction.seconds >= 0; })
console.log("array length should be 0: ", Auction.auctions.length)

Beachten Sie, dass dadurch das ursprüngliche Array geändert wird, anstatt ein neues Array zu erstellen. es an einem Ort wie diesem zu tun, kann vorteilhaft sein, z. in dem Fall, dass das Array der einzige Speicherengpass des Programms ist; In diesem Fall möchten Sie nicht einmal vorübergehend ein anderes Array derselben Größe erstellen.

2
Don Hatch

Es gibt bereits viele wunderbare Antworten in diesem Thread. Ich wollte jedoch meine Erfahrung teilen, als ich versuchte, "das n-te Element aus dem Array entfernen" im ES5-Kontext zu lösen. 

JavaScript-Arrays verfügen über verschiedene Methoden, um Elemente am Anfang oder Ende hinzuzufügen/zu entfernen. Diese sind:

arr.Push(ele) - To add element(s) at the end of the array 
arr.unshift(ele) - To add element(s) at the beginning of the array
arr.pop() - To remove last element from the array 
arr.shift() - To remove first element from the array 

Im Wesentlichen kann keine der obigen Methoden direkt verwendet werden, um das n-te Element aus dem Array zu entfernen. 

Es ist erwähnenswert, dass dies im Gegensatz zu Java Iterators .__ steht. mit dem es möglich ist, das n-te Element für eine Sammlung zu entfernen während iterieren.

Dies führt im Wesentlichen zu einer einzigen Array-Methode Array.splice, um das n-te Element zu entfernen (es gibt andere Dinge, die Sie auch mit diesen Methoden machen könnten, aber im Rahmen dieser Frage konzentriere ich mich auf das Entfernen von Elementen):

Array.splice(index,1) - removes the element at the index 

Hier ist der Code, der von der ursprünglichen Antwort (mit Kommentaren) kopiert wurde:

var arr = ["one", "two", "three", "four"];
var i = arr.length; //initialize counter to array length 

while (i--) //decrement counter else it would run into IndexOutBounds exception
{
  if (arr[i] === "four" || arr[i] === "two") {
    //splice modifies the original array
    arr.splice(i, 1); //never runs into IndexOutBounds exception 
    console.log("Element removed. arr: ");

  } else {
    console.log("Element not removed. arr: ");
  }
  console.log(arr);
}

Eine andere bemerkenswerte Methode ist Array.slice. Der Rückgabetyp dieser Methode sind jedoch die entfernten Elemente. Auch das ursprüngliche Array wird dadurch nicht verändert. Modifizierter Code-Ausschnitt wie folgt:

var arr = ["one", "two", "three", "four"];
var i = arr.length; //initialize counter to array length 

while (i--) //decrement counter 
{
  if (arr[i] === "four" || arr[i] === "two") {
    console.log("Element removed. arr: ");
    console.log(arr.slice(i, i + 1));
    console.log("Original array: ");
    console.log(arr);
  }
}

Trotzdem können wir Array.slice verwenden, um das n-te Element auf folgende Weise zu entfernen. Wie Sie sehen können, ist dies viel mehr Code (daher ineffizient). 

var arr = ["one", "two", "three", "four"];
var i = arr.length; //initialize counter to array length 

while (i--) //decrement counter 
{
  if (arr[i] === "four" || arr[i] === "two") {
    console.log("Array after removal of ith element: ");
    arr = arr.slice(0, i).concat(arr.slice(i + 1));
    console.log(arr);
  }

}

Die Array.slice-Methode ist äußerst wichtig, um .__ zu erreichen. Unveränderlichkeit in der funktionalen Programmierung à la redux

1
Bhanuprakash D
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--;
    }
}
1
Dmitry Ragozin

Versuchen Sie, ein Array an newArray weiterzuleiten,

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;
0
Zon

Sie können nur durchsehen und shift()

0
user8533067