web-dev-qa-db-fra.com

Séparer un tableau Javascript à partir du rappel transmis à forEach

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.

16
Gus Hogg-Blake

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' ]
32
thefourtheye

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 .

12
pasty

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.

1
Elvinn

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