624 votes

Boucle dans un tableau et suppression des éléments, sans interrompre la boucle for.

J'ai la boucle for suivante, et lorsque j'utilise la fonction splice() pour supprimer un élément, j'obtiens alors que 'seconds' est indéfini. Je pourrais vérifier s'il est indéfini, mais je pense qu'il y a probablement un moyen plus élégant de le faire. L'objectif est de supprimer simplement un élément 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);
    }           
}

14 votes

En plus de l'itération en arrière et de l'ajustement de la longueur, vous pouvez aussi simplement placer les membres que vous voulez dans un nouveau tableau.

3 votes

Pourquoi dites-vous Auction.auctions[i]['seconds']-- au lieu de auction.seconds-- ?

0 votes

Vous voudrez probablement regarder dans la fonction prédéfinie .shift() ;

1050voto

squint Points 28293

Le tableau est réindexé quand vous faites un .splice() ce qui signifie que vous sauterez un index lorsqu'il sera supprimé, et que votre cache .length est obsolète.

Pour le corriger, il faut soit décrémenter i après un .splice() ou simplement itérer en sens inverse...

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

De cette façon, la réindexation n'affecte pas l'élément suivant dans l'itération, puisque l'indexation n'affecte que les éléments allant du point actuel à la fin du tableau, et que l'élément suivant dans l'itération est inférieur au point actuel.

1 votes

Je me demande si length === 0 se retrouverait dans une boucle infinie, j'ai essayé cette solution et (bien sûr, elle fonctionne), puisqu'elle évalue d'abord la valeur de i puis décrémente. Cependant, -- (et ++ ) sont si bizarres dans leur comportement qu'un langage moderne comme swift a cessé de les supporter. Je pense que c'est une mauvaise pratique (du moins dans un tel contexte).

7 votes

@lukas_o Il n'y a pas de bizarrerie ou de fonctionnalité inattendue si vous comprenez simplement ce que cela signifie. i++ signifie évaluer la valeur, puis l'incrémenter. ++i signifie incrémenter la valeur, puis l'évaluer. JS ne fera jamais autre chose que cela. C'est très facile à comprendre et il est garanti que cela fonctionnera exactement de la même manière à chaque fois, même si vous utilisez un autre moteur JS.

244voto

frattaro Points 96

C'est un problème assez courant. La solution est de faire une boucle en arrière :

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

Peu importe que vous les détachiez de la fin, car les indices seront préservés au fur et à mesure que vous reculerez.

1 votes

Cette idée de boucle inversée a sauvé ma journée. Merci

59voto

Marc Points 4418

Recalculez la longueur à chaque fois que vous parcourez la boucle au lieu de ne le faire qu'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
      }
}

De cette façon, vous ne dépasserez pas les limites.

EDIT : ajout d'un décrément dans l'instruction if.

54voto

0xc0de Points 2026

Bien que votre question porte sur la suppression d'éléments de l'application le tableau sur lequel on itère et non de retirer des éléments (en plus d'autres traitements) de manière efficace, je pense qu'il faut y réfléchir si l'on se trouve dans une situation similaire.

La complexité algorithmique de cette approche est O(n^2) car la fonction splice et la boucle for itèrent toutes deux sur le tableau (la fonction splice déplace tous les éléments du tableau dans le pire des cas). Au lieu de cela, vous pouvez simplement pousser les éléments requis vers le nouveau tableau et ensuite assigner ce tableau à la variable désirée (qui vient d'être 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 faire tenir tout ça dans une ligne :

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

27voto

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X