Boucle à travers le tableau et en enlevant des éléments, sans rompre pour boucle

j'ai ce qui suit pour loop, et quand j'utilise splice() pour supprimer un élément, j'obtiens que "secondes" est indéfini. Je pourrais vérifier si c'est indéfini, mais je pense qu'il y a probablement une façon plus élégante de faire ça. Le désir est simplement de supprimer un article et de continuer.

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);
    }           
}
359
demandé sur JJJ 2012-03-27 05:44:07
la source

11 ответов

le tableau est ré-indexé quand vous faites un .splice() , ce qui signifie que vous sauterez un index quand un est supprimé, et votre cache .length est obsolète.

pour le corriger, vous devez soit décrémenter i après un .splice() , ou tout simplement itérer à l'envers...

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

de cette façon, la réindexation n'affecte pas le point suivant de l'itération, puisque l'indexation affecte seulement les points du point courant au point fin du Tableau, et l'élément suivant dans l'itération est plus bas que le point courant.

699
répondu 4 revs, 3 users 76%user1106925 2017-12-08 20:03:16
la source

C'est un problème assez courant. La solution est de boucler la boucle à l'envers:

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

cela n'a pas d'importance si vous les enlevez de la fin parce que les indices seront préservés pendant que vous faites marche arrière.

95
répondu frattaro 2015-01-24 06:49:17
la source

recalculer la longueur à chaque fois à travers la boucle au lieu de juste au début, par exemple:

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

comme ça tu ne dépasseras pas les limites.

EDIT: ajout d'une diminution de l'instruction if.

36
répondu Marc 2012-03-27 05:50:20
la source

bien que votre question porte sur la suppression d'éléments de le tableau étant itéré sur et non sur la suppression d'éléments (en plus d'un autre traitement) efficacement, je pense que l'on devrait reconsidérer si dans une situation similaire.

la complexité algorithmique de cette approche est O(n^2) comme fonction splice et la boucle for les deux itèrent sur le tableau (la fonction splice déplace tous les éléments du tableau dans le pire des cas). Au lieu de cela, vous il suffit de pousser les éléments requis vers le nouveau tableau, puis d'assigner ce tableau à la variable désirée (qui a été juste itérée).

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;

depuis ES2015, nous pouvons utiliser Array.prototype.filter pour l'ajuster en une seule ligne:

Auction.auctions = Auction.auctions.filter(auction => --auction.seconds >= 0);
20
répondu 0xc0de 2018-09-26 19:28:36
la source
Auction.auction = Auction.auctions.filter(function(el) {
  return --el["seconds"] > 0;
});
16
répondu Aesthete 2013-09-06 12:12:44
la source

voici un autre exemple pour l'utilisation correcte de splice. Cet exemple est sur le point de supprimer 'attribut' de 'array'.

for (var i = array.length; i--;) {
    if (array[i] === 'attribute') {
        array.splice(i, 1);
    }
}
9
répondu daniel.szaniszlo 2016-02-26 15:34:38
la source

une autre solution simple pour digérer un tableau éléments une fois:

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
répondu Pablo 2016-11-17 19:40:28
la source

si vous utilisez E avec ES6+ - pourquoi ne pas simplement utiliser Array.méthode de filtre?

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

noter que la modification de l'élément tableau pendant l'itération du filtre ne fonctionne que pour les objets et ne fonctionnera pas pour le tableau des valeurs primitives.

3
répondu Rubinsh 2017-11-29 10:28:04
la source
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
répondu Dmitry Ragozin 2016-12-15 18:33:46
la source

Essayez de relayer un tableau en newArray si la boucle:

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
répondu Zon 2016-01-28 18:54:43
la source

vous pouvez simplement regarder à travers et utiliser shift()

0
répondu user8533067 2017-08-29 17:41:21
la source

Autres questions sur javascript loops