Como remover o elemento da matriz no loop forEach?


97

Estou tentando remover um elemento em uma matriz em um forEachloop, mas estou tendo problemas com as soluções padrão que vi.

Isso é o que estou tentando no momento:

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

Eu sei que está entrando no ifporque estou vendo YippeeeeeE!!!!!!!!!!!!!no console.

MEU PROBLEMA: Eu sei que meu loop for e se a lógica estão corretos, mas minha tentativa de remover o elemento atual do array está falhando.

ATUALIZAR:

Tentei a resposta do Xotic750 e o elemento ainda não foi removido:

Aqui está a função em meu código:

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

Aqui está a saída em que a matriz ainda não foi removida:

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

Obviamente, isso vai para a instrução if conforme direcionado, mas também é óbvio que o [• • •] ainda está lá.


8
Existe um motivo pelo qual você está usando forEach? Se você deseja remover itens, a função mais adequada é filter.
Jon

2
Não se você precisar manter a referência ao array original.
Xotic750

Sim, gostaríamos de manter a referência ao array original.
novicePrgrmr

Não ficou claro com sua pergunta, qual é o problema real que você está tendo? Você pode dar um exemplo, talvez um jsFiddle? Parece que talvez você deva usar o indexatributo em vez do itemseusplice
Xotic750

@ Xotic750 Desculpe, esclarecimento adicionado.
novicePrgrmr

Respostas:


235

Parece que você está tentando fazer isso?

Repita e modifique uma matriz usando 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>

O que funciona bem para o caso simples em que você não tem 2 dos mesmos valores dos itens de array adjacentes, caso contrário, você terá esse 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>

Então, o que podemos fazer sobre esse problema ao iterar e transformar um array? Bem, a solução usual é trabalhar ao contrário. Usando ES3 enquanto, mas você pode usar para açúcar, se preferir

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, mas você queria usar métodos de iteração ES5. Bem, a opção seria usar Array.prototype.filter, mas isso não altera o array original, mas cria um novo, portanto, embora você possa obter a resposta correta, não é o que parece ter especificado.

Também poderíamos usar ES5 Array.prototype.reduceRight , não por sua propriedade de redução ao invés de sua propriedade de iteração, ou seja, iterar ao contrário.

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>

Ou podemos usar ES5 Array.protoype.indexOf assim.

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>

Mas você deseja especificamente usar ES5 Array.prototype.forEach , então o que podemos fazer? Bem, precisamos usar Array.prototype.slice para fazer uma cópia superficial do array e Array.prototype.reverse para que possamos trabalhar ao contrário para transformar o array original.

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>

Finalmente o ES6 nos oferece algumas alternativas adicionais, onde não precisamos fazer cópias rasas e revertê-las. Notavelmente, podemos usar Geradores e Iteradores . No entanto, o suporte é bastante baixo no momento.

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>

Algo a se notar em tudo o que foi dito acima é que, se você estivesse removendo NaN do array, comparar com igual não funcionaria porque em Javascript NaN === NaNé falso. Mas vamos ignorar isso nas soluções, pois é mais um caso extremo não especificado.

Então aí está, uma resposta mais completa com soluções que ainda têm casos extremos. O primeiro exemplo de código ainda está correto, mas, conforme declarado, não é sem problemas.


Obrigado por responder. Tentei usar sua solução, mas ainda não está removendo o elemento da matriz. Vou colocar os detalhes na pergunta.
novicePrgrmr

Coloque console.log(review);depois de forEach, como no meu exemplo.
Xotic750

4
Cuidado, isso quebra se dois elementos consecutivos forem excluídos: var review = ['a', 'a', 'c', 'b', 'a']; renderá ['a', 'c', 'b']
quentinadam

3
NOTA - Esta resposta está ERRADA! O foreach itera pela matriz por índice. Depois de excluir os elementos durante a iteração do índice de alterações de itens a seguir. Neste exemplo, depois de excluir o primeiro 'a', o número de índice 1 agora se torna 'c'. Portanto, o primeiro 'b' nem mesmo é avaliado. Já que você não tentou excluí-lo, simplesmente aconteceu de estar ok, mas não é esse o caminho. Você deve iterar por meio de uma cópia reversa da matriz e, em seguida, excluir os itens da matriz original.
danbars de

4
@ Xotic750 - a resposta original (agora sendo o primeiro fragmento de código) está errada simplesmente porque forEach não fará um loop por todos os elementos do array, como expliquei no comentário anterior. Eu sei que a questão era como remover elementos em um loop forEach, mas a resposta simples é que você não faz isso. Como muitas pessoas estão lendo essas respostas e, muitas vezes, copiando-as cegamente (respostas especialmente aceitas), é importante observar as falhas no código. Acho que o loop while reverso é a solução mais simples, mais eficiente e mais legível e deve ser a resposta aceita
danbars

37

Use em Array.prototype.filtervez de 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

Embora a resposta do Xotic750 forneça vários pontos bons e soluções possíveis, às vezes simples é melhor .

Você sabe que a matriz que está sendo iterada está sofrendo mutação na própria iteração (ou seja, removendo um item => alterações de índice), portanto, a lógica mais simples é retroceder em um modo antigo for(à la linguagem 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 você realmente pensar sobre isso, a forEaché apenas um açúcar sintático para um forloop ... Então, se não está te ajudando, por favor, pare de quebrar sua cabeça contra isso.


1

Você também pode usar indexOf para fazer isso

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

1

Eu entendi que você deseja remover da matriz usando uma condição e ter outra matriz que possui itens removidos da matriz. Está certo?

Que tal agora?

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

Espero que ajude ...

A propósito, comparei 'for-loop' com 'forEach'.

Se remove no caso de uma string conter 'f', o resultado é diferente.

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 remova a cada iteração, também um resultado é diferente.

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

A seguir, você encontrará todos os elementos que não são iguais aos seus personagens especiais!

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

0

Aqui está como você deve fazer isso:

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

1
Eu não acho que seja o caso. Mudei meu código presumindo que p fosse um índice e agora ele nem mesmo está entrando na ifinstrução.
novicePrgrmr

3
@WhoCares Você deve ver a especificação ecma-international.org/ecma-262/5.1/#sec-15.4.4.18 Os argumentos da função callBack sãoitem, index, object
Xotic750
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.