Come rimuovere l'elemento dall'array nel ciclo forEach?


97

Sto cercando di rimuovere un elemento in un array in un forEachciclo, ma ho problemi con le soluzioni standard che ho visto.

Questo è quello che sto provando attualmente:

review.forEach(function(p){
   if(p === '\u2022 \u2022 \u2022'){
      console.log('YippeeeE!!!!!!!!!!!!!!!!')
      review.splice(p, 1);
   }
});

So che sta entrando nel ifperché lo vedo YippeeeeeE!!!!!!!!!!!!!nella console.

IL MIO PROBLEMA: So che il mio ciclo for e la logica if sono valide, ma il mio tentativo di rimuovere l'elemento corrente dall'array fallisce.

AGGIORNARE:

Ho provato la risposta di Xotic750 e l'elemento non viene ancora rimosso:

Ecco la funzione nel mio codice:

review.forEach(function (item, index, object) {
    if (item === '\u2022 \u2022 \u2022') {
       console.log('YippeeeE!!!!!!!!!!!!!!!!')
       object.splice(index, 1);
    }
    console.log('[' + item + ']');
});

Ecco l'output in cui l'array non è ancora stato rimosso:

[Scott McNeil]
[reviewed 4 months ago]
[ Mitsubishi is AMAZING!!!]
YippeeeE!!!!!!!!!!!!!!!!
[•  •]

Quindi ovviamente sta entrando nell'istruzione if come indicato, ma è anche ovvio che [• • •] è ancora lì.


8
C'è un motivo per cui stai usando forEach? Se desideri rimuovere elementi, la funzione più appropriata è filter.
Jon

2
No, se è necessario mantenere il riferimento all'array originale.
Xotic750

Sì, vorremmo mantenere il riferimento all'array originale.
novicePrgrmr

Non è chiaro dalla tua domanda, qual è il vero problema che stai riscontrando? Puoi fare un esempio, forse un jsFiddle? Sembra che forse dovresti usare l' indexattributo piuttosto che itemper il tuosplice
Xotic750

@ Xotic750 Spiacente, chiarimento aggiunto.
novicePrgrmr

Risposte:


235

Sembra che tu stia cercando di farlo?

Itera e modifica un array utilizzando Array.prototype.splice

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var review = ['a', 'b', 'c', 'b', 'a'];

review.forEach(function(item, index, object) {
  if (item === 'a') {
    object.splice(index, 1);
  }
});

log(review);
<pre id="out"></pre>

Che funziona bene per un semplice caso in cui non si hanno 2 degli stessi valori degli elementi dell'array adiacenti, altrimenti si ha questo problema.

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var review = ['a', 'a', 'b', 'c', 'b', 'a', 'a'];

review.forEach(function(item, index, object) {
  if (item === 'a') {
    object.splice(index, 1);
  }
});

log(review);
<pre id="out"></pre>

Quindi cosa possiamo fare per risolvere questo problema durante l'iterazione e la modifica di un array? La solita soluzione è lavorare al contrario. Usando ES3 mentre ma potresti usare per lo zucchero se preferisci

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var review = ['a' ,'a', 'b', 'c', 'b', 'a', 'a'],
  index = review.length - 1;

while (index >= 0) {
  if (review[index] === 'a') {
    review.splice(index, 1);
  }

  index -= 1;
}

log(review);
<pre id="out"></pre>

Ok, ma volevi usare i metodi di iterazione ES5. Bene, l'opzione sarebbe quella di usare Array.prototype.filter ma questo non modifica l'array originale ma ne crea uno nuovo, quindi mentre puoi ottenere la risposta corretta, non è quello che sembra aver specificato.

Potremmo anche usare ES5 Array.prototype.reduceRight , non per la sua proprietà riducente ma piuttosto per la sua proprietà di iterazione, cioè iterare al contrario.

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var review = ['a', 'a', 'b', 'c', 'b', 'a', 'a'];

review.reduceRight(function(acc, item, index, object) {
  if (item === 'a') {
    object.splice(index, 1);
  }
}, []);

log(review);
<pre id="out"></pre>

Oppure potremmo usare ES5 Array.protoype.indexOf in questo modo.

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var review = ['a', 'a', 'b', 'c', 'b', 'a', 'a'],
  index = review.indexOf('a');

while (index !== -1) {
  review.splice(index, 1);
  index = review.indexOf('a');
}

log(review);
<pre id="out"></pre>

Ma in particolare si desidera utilizzare ES5 Array.prototype.forEach , quindi cosa possiamo fare? Bene, dobbiamo usare Array.prototype.slice per creare una copia superficiale dell'array e Array.prototype.reverse in modo da poter lavorare al contrario per mutare l'array originale.

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var review = ['a', 'a', 'b', 'c', 'b', 'a', 'a'];

review.slice().reverse().forEach(function(item, index, object) {
  if (item === 'a') {
    review.splice(object.length - 1 - index, 1);
  }
});

log(review);
<pre id="out"></pre>

Infine ES6 ci offre alcune ulteriori alternative, in cui non è necessario fare copie superficiali e invertirle. In particolare possiamo usare generatori e iteratori . Tuttavia, al momento il supporto è piuttosto basso.

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

function* reverseKeys(arr) {
  var key = arr.length - 1;

  while (key >= 0) {
    yield key;
    key -= 1;
  }
}

var review = ['a', 'a', 'b', 'c', 'b', 'a', 'a'];

for (var index of reverseKeys(review)) {
  if (review[index] === 'a') {
    review.splice(index, 1);
  }
}

log(review);
<pre id="out"></pre>

Qualcosa da notare in tutto quanto sopra è che, se si rimuove NaN dall'array, il confronto con gli uguali non funzionerà perché in Javascript NaN === NaNè falso. Ma lo ignoreremo nelle soluzioni in quanto è un altro caso limite non specificato.

Quindi eccolo, una risposta più completa con soluzioni che hanno ancora casi limite. Il primo esempio di codice è ancora corretto ma, come affermato, non è privo di problemi.


Grazie per aver risposto. Ho provato a utilizzare la tua soluzione, ma ancora non sta rimuovendo l'elemento dall'array. Metterò i dettagli nella domanda.
novicePrgrmr

Metti console.log(review);dopo il forEach, come nel mio esempio.
Xotic750

4
Attenzione, questo si interrompe se due elementi consecutivi da eliminare: var review = ['a', 'a', 'c', 'b', 'a']; produrrà ['a', 'c', 'b']
quentinadam

3
NOTA - Questa risposta è SBAGLIATA! Il foreach esegue l'iterazione della matrice in base all'indice. Una volta eliminati gli elementi durante l'iterazione dell'indice delle seguenti modifiche agli elementi. In questo esempio, una volta eliminata la prima "a", il numero di indice 1 ora diventa "c". Pertanto la prima 'b' non viene nemmeno valutata. Dato che non hai provato a cancellarlo, è andato tutto bene, ma non è così. È necessario iterare attraverso una copia inversa della matrice e quindi eliminare gli elementi nella matrice originale.
danbars

4
@ Xotic750 - la risposta originale (ora essendo il primo frammento di codice) è sbagliata semplicemente perché forEach non eseguirà un ciclo attraverso tutti gli elementi dell'array, come ho spiegato nel commento precedente. So che la domanda era come rimuovere gli elementi in un ciclo forEach, ma la risposta semplice è che non lo fai. Poiché molte persone leggono queste risposte e molte volte copiano ciecamente le risposte (soprattutto quelle accettate), è importante notare i difetti nel codice. Penso che il ciclo while inverso sia la soluzione più semplice, più efficiente e più leggibile e dovrebbe essere la risposta accettata
danbars

37

Utilizzare Array.prototype.filterinvece di forEach:

var pre = document.getElementById('out');

function log(result) {
  pre.appendChild(document.createTextNode(result + '\n'));
}

var review = ['a', 'b', 'c', 'b', 'a', 'e'];
review = review.filter(item => item !== 'a');
log(review);

10

Sebbene la risposta di Xotic750 fornisca diversi buoni punti e possibili soluzioni, a volte semplice è migliore .

Sai che l'array su cui si sta iterando viene mutato nell'iterazione stessa (ovvero rimozione di un elemento => modifiche all'indice), quindi la logica più semplice è tornare indietro in un vecchio stile for( linguaggio à la C ):

let arr = ['a', 'a', 'b', 'c', 'b', 'a', 'a'];

for (let i = arr.length - 1; i >= 0; i--) {
  if (arr[i] === 'a') {
    arr.splice(i, 1);
  }
}

document.body.append(arr.join());

Se ci pensi davvero, a forEachè solo zucchero sintattico per un forciclo ... Quindi se non ti sta aiutando, per favore smettila di romperti la testa contro di esso.


1

Puoi anche usare indexOf invece per farlo

var i = review.indexOf('\u2022 \u2022 \u2022');
if (i !== -1) review.splice(i,1);

1

Ho capito che vuoi rimuovere dall'array usando una condizione e avere un altro array che ha elementi rimossi dall'array. È giusto?

Cosa ne pensi di questo?

var review = ['a', 'b', 'c', 'ab', 'bc'];
var filtered = [];
for(var i=0; i < review.length;) {
  if(review[i].charAt(0) == 'a') {
    filtered.push(review.splice(i,1)[0]);
  }else{
    i++;
  }
}

console.log("review", review);
console.log("filtered", filtered);

Spero che questo aiuto ...

A proposito, ho confrontato "for-loop" con "forEach".

Se rimuovi nel caso in cui una stringa contenga "f", il risultato è diverso.

var review = ["of", "concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "flatMap", "flatten", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toSource", "toString", "unshift", "values"];
var filtered = [];
for(var i=0; i < review.length;) {
  if( review[i].includes('f')) {
    filtered.push(review.splice(i,1)[0]);
  }else {
    i++;
  }
}
console.log("review", review);
console.log("filtered", filtered);
/**
 * review [  "concat",  "copyWithin",  "entries",  "every",  "includes",  "join",  "keys",  "map",  "pop",  "push",  "reduce",  "reduceRight",  "reverse",  "slice",  "some",  "sort",  "splice",  "toLocaleString",  "toSource",  "toString",  "values"] 
 */

console.log("========================================================");
review = ["of", "concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "flatMap", "flatten", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toSource", "toString", "unshift", "values"];
filtered = [];

review.forEach(function(item,i, object) {
  if( item.includes('f')) {
    filtered.push(object.splice(i,1)[0]);
  }
});

console.log("-----------------------------------------");
console.log("review", review);
console.log("filtered", filtered);

/**
 * review [  "concat",  "copyWithin",  "entries",  "every",  "filter",  "findIndex",  "flatten",  "includes",  "join",  "keys",  "map",  "pop",  "push",  "reduce",  "reduceRight",  "reverse",  "slice",  "some",  "sort",  "splice",  "toLocaleString",  "toSource",  "toString",  "values"]
 */

E rimuovi per ogni iterazione, anche un risultato è diverso.

var review = ["of", "concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "flatMap", "flatten", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toSource", "toString", "unshift", "values"];
var filtered = [];
for(var i=0; i < review.length;) {
  filtered.push(review.splice(i,1)[0]);
}
console.log("review", review);
console.log("filtered", filtered);
console.log("========================================================");
review = ["of", "concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "flatMap", "flatten", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toSource", "toString", "unshift", "values"];
filtered = [];

review.forEach(function(item,i, object) {
  filtered.push(object.splice(i,1)[0]);
});

console.log("-----------------------------------------");
console.log("review", review);
console.log("filtered", filtered);


0

Quanto segue ti darà tutti gli elementi che non sono uguali ai tuoi caratteri speciali!

review = jQuery.grep( review, function ( value ) {
    return ( value !== '\u2022 \u2022 \u2022' );
} );

0

Ecco come dovresti farlo:

review.forEach(function(p,index,object){
   if(review[index] === '\u2022 \u2022 \u2022'){
      console.log('YippeeeE!!!!!!!!!!!!!!!!')
      review.splice(index, 1);
   }
});

1
Non credo sia il caso. Ho cambiato il mio codice supponendo che p fosse un indice, e ora non sta nemmeno entrando ifnell'istruzione.
novicePrgrmr

3
@WhoCares Dovreste vedere le specifiche ecma-international.org/ecma-262/5.1/#sec-15.4.4.18 callback argomenti della funzione sonoitem, index, object
Xotic750
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.