J'ai ce code qui est supposé itérer sur chaque élément d'un tableau, en supprimant les éléments en fonction de certaines conditions:
//iterate over all items in an array
//if the item is "b", remove it.
var array = ["a", "b", "c"];
array.forEach(function(item) {
if(item === "b") {
array.splice(array.indexOf(item), 1);
}
console.log(item);
});
Sortie désirée:
a
b
c
Sortie réelle:
a
b
Il est évident que la méthode forEach native ne vérifie pas après chaque itération si l'élément a été supprimé. Si c'est le cas, l'élément suivant est ignoré. Existe-t-il un meilleur moyen de le faire, outre le remplacement de la méthode forEach ou l'implémentation de ma propre classe à utiliser à la place d'un tableau?
Edit - suite à mon commentaire, je suppose que la solution consiste simplement à utiliser un standard pour la boucle. N'hésitez pas à répondre si vous avez un meilleur moyen.
Voyons pourquoi JavaScript se comporte comme ceci. Selon la spécification standard ECMAScript pour Array.prototype.forEach
,
lorsque vous supprimez un élément à l'index 1, l'élément à l'index 2 devient l'élément à l'index 1 et l'index 2 n'existe pas pour cet objet.
Désormais, JavaScript recherche l'élément 2 dans l'objet, qui n'est pas trouvé, donc il ignore l'appel de fonction.
C'est pourquoi vous ne voyez que a
et b
.
Pour ce faire, utilisez Array.prototype.filter
var array = ["a", "b", "c"];
array = array.filter(function(currentChar) {
console.log(currentChar); // a, b, c on separate lines
return currentChar !== "b";
});
console.log(array); // [ 'a', 'c' ]
Une possibilité serait d'utiliser la fonction array.slice(0)
, qui crée une copie ( clone ) du tableau et donc l'itération est séparée de la suppression.
Ensuite, le seul changement à l’approche originale utilisant array.forEach
serait de le changer en array.slice(0).forEach
et cela fonctionnera:
array.slice(0).forEach(function(item) {
if(item === "b") {
array.splice(array.indexOf(item), 1);
}
alert(item)
});
Après le forEach, le tableau ne contiendra que a
et b
.
Une démo de jsFiddle peut être trouvée ici .
Une autre possibilité serait d'utiliser la fonction array.reduceRight
pour éviter le saut:
//iterate over all items in an array from right to left
//if the item is "b", remove it.
const array = ["a", "b", "c"];
array.reduceRight((_, item, i) => {
if(item === "b") {
array.splice(i, 1);
}
});
console.log(array);
Après la reduceRight
, le tableau ne contiendra que a
et c
.
Utiliser Array.prototype.filter
comme dans la réponse de thefourtheye est une bonne solution, mais vous pouvez également le faire avec une boucle while
. Par exemple.:
const array = ["a", "b", "c"];
let i = 0;
while (i < array.length) {
const item = array[i];
if (item === "b") {
array.splice(i, 1);
} else {
i += 1;
}
console.log(item);
});