Jak usunąć element z tablicy w pętli forEach?

98

Próbuję usunąć element z tablicy w pliku forEach pętli, ale mam problemy ze standardowymi rozwiązaniami, które widziałem.

Oto, czego obecnie próbuję:

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

Wiem, że wchodzi w to, ifponieważ widzę YippeeeeeE!!!!!!!!!!!!!w konsoli.

MÓJ PROBLEM: Wiem, że moja pętla for i logika są prawidłowe, ale moja próba usunięcia bieżącego elementu z tablicy kończy się niepowodzeniem.

AKTUALIZACJA:

Wypróbowałem odpowiedź Xotic750, a element nadal nie jest usuwany:

Oto funkcja w moim kodzie:

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

Oto dane wyjściowe, w których tablica nadal nie jest usuwana:

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

Więc oczywiście wchodzi w instrukcję if zgodnie z zaleceniami, ale jest również oczywiste, że [• • •] wciąż tam jest.

nowicjuszPrgrmr
źródło
8
Czy jest jakiś powód, dla którego używasz forEach? Jeśli chcesz usunąć elementy, najbardziej odpowiednią funkcją jest filter.
Jon
2
Nie, jeśli chcesz zachować odniesienie do oryginalnej tablicy.
Xotic750,
Tak, chcielibyśmy zachować odniesienie do oryginalnej tablicy.
nowicjusz Prgrmr
Z twojego pytania nie wynika jasno, jaki jest rzeczywisty problem, który masz? Czy możesz podać przykład, może jsFiddle? Wygląda na to, że być może powinieneś używać tego indexatrybutu, a nie itemswojegosplice
Xotic750
@ Xotic750 Przepraszamy, dodano wyjaśnienie.
nowicjusz Prgrmr

Odpowiedzi:

236

Wygląda na to, że próbujesz to zrobić?

Wykonaj iterację i mutuj tablicę za pomocą 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>

Co działa dobrze w prostym przypadku, gdy nie masz 2 takich samych wartości jak sąsiednie elementy tablicy, w przeciwnym razie masz ten problem.

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>

Więc co możemy zrobić z tym problemem podczas iteracji i mutacji tablicy? Cóż, zwykłym rozwiązaniem jest praca w odwrotnej kolejności. Używasz ES3 , ale możesz użyć do cukru, jeśli wolisz

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, ale chciałeś użyć metod iteracji ES5. Cóż, opcją byłoby użycie Array.prototype.filter, ale to nie zmienia oryginalnej tablicy, ale tworzy nową, więc chociaż możesz uzyskać poprawną odpowiedź, nie jest to to, co wydaje się być określone.

Moglibyśmy również użyć ES5 Array.prototype.reduceRight , nie dla jego właściwości redukującej, a raczej jej właściwości iteracji, tj. Iteracji w odwrotnej kolejności.

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>

Lub możemy użyć ES5 Array.protoype.indexOf w ten sposób.

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>

Ale konkretnie chcesz użyć ES5 Array.prototype.forEach , więc co możemy zrobić? Cóż, musimy użyć Array.prototype.slice, aby utworzyć płytką kopię tablicy i Array.prototype.reverse , abyśmy mogli pracować w odwrotnej kolejności, aby zmutować oryginalną tablicę.

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>

Wreszcie ES6 oferuje nam dalsze alternatywy, w przypadku których nie musimy wykonywać płytkich kopii i odwracać ich. W szczególności możemy używać generatorów i iteratorów . Jednak obecnie poparcie jest dość niskie.

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>

Coś, na co należy zwrócić uwagę w powyższym, polega na tym, że jeśli usuwałeś NaN z tablicy, porównywanie z równymi sobie nie zadziała, ponieważ w JavascriptNaN === NaN jest fałszywe. Ale zamierzamy zignorować to w rozwiązaniach, ponieważ jest to kolejny nieokreślony przypadek skrajny.

Mamy więc pełniejszą odpowiedź z rozwiązaniami, które wciąż mają skrajne przypadki. Pierwszy przykład kodu jest nadal poprawny, ale jak wspomniano, nie jest pozbawiony problemów.

Xotic750
źródło
Dzięki za odpowiedź. Próbowałem użyć twojego rozwiązania, ale nadal nie usuwa ono elementu z tablicy. Szczegóły podam w pytaniu.
nowicjusz Prgrmr
Umieść console.log(review);po forEach, jak w moim przykładzie.
Xotic750
4
Ostrożnie, to się psuje, jeśli dwa kolejne elementy do usunięcia: var review = ['a', 'a', 'c', 'b', 'a']; da ['a', 'c', 'b']
quentinadam
4
UWAGA - ta odpowiedź jest ZŁA! Foreach iteruje tablicę według indeksu. Po usunięciu elementów podczas iteracji zmienia się indeks następujących elementów. W tym przykładzie, po usunięciu pierwszego „a”, indeks numer 1 staje się teraz „c”. Dlatego pierwsze „b” nie jest nawet oceniane. Ponieważ nie próbowałeś go usunąć, po prostu tak się złożyło, ale nie w ten sposób. Powinieneś wykonać iterację przez odwrotną kopię tablicy, a następnie usunąć elementy z oryginalnej tablicy.
danbars
4
@ Xotic750 - pierwotna odpowiedź (będąca teraz pierwszym fragmentem kodu) jest błędna po prostu dlatego, że forEach nie przejdzie przez wszystkie elementy tablicy, jak wyjaśniłem w poprzednim komentarzu. Wiem, że chodziło o to, jak usunąć elementy w pętli forEach, ale prosta odpowiedź jest taka, że ​​tego nie robisz. Ponieważ wiele osób czyta te odpowiedzi i wiele razy ślepo kopiuje odpowiedzi (szczególnie te akceptowane), ważne jest, aby zanotować błędy w kodzie. Myślę, że odwrotna pętla while jest najprostszym, najbardziej wydajnym i najbardziej czytelnym rozwiązaniem i powinna być akceptowaną odpowiedzią
danbars
37

Użyj Array.prototype.filterzamiast 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);
alemjerus
źródło
11

Mimo że odpowiedź Xotic750 zawiera kilka dobrych punktów i możliwych rozwiązań, czasami proste jest lepsze .

Wiesz, że tablica, na której jest iterowana, jest mutowana w samej iteracji (tj. Usuwaniu elementu => zmiany indeksu), dlatego najprostszą logiką jest cofnięcie się w staroświeckim for(języku à 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());

Jeśli naprawdę o tym myślisz, to forEachjest po prostu cukier syntaktyczny dla forpętli ... Więc jeśli ci to nie pomaga, po prostu przestań łamać sobie głowę przeciwko temu.

CPHPython
źródło
1

Aby to zrobić, możesz również użyć indexOf

var i = review.indexOf('\u2022 \u2022 \u2022');
if (i !== -1) review.splice(i,1);
jj689
źródło
1

Zrozumiałem, że chcesz usunąć z tablicy przy użyciu warunku i mieć inną tablicę, która ma usunięte elementy z tablicy. Jest w porządku?

Co powiesz na to?

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

Mam nadzieję, że to pomoże...

Przy okazji porównałem „for-loop” z „forEach”.

Jeśli usuniesz w przypadku, gdy ciąg zawiera „f”, wynik jest inny.

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"]
 */

I usuń po każdej iteracji, również wynik jest inny.

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

Park Jun-Hong
źródło
0

Poniżej znajdziesz wszystkie elementy, które nie są równe twoim znakom specjalnym!

review = jQuery.grep( review, function ( value ) {
    return ( value !== '\u2022 \u2022 \u2022' );
} );
Jenna Leaf
źródło
0

Oto jak powinieneś to zrobić:

review.forEach(function(p,index,object){
   if(review[index] === '\u2022 \u2022 \u2022'){
      console.log('YippeeeE!!!!!!!!!!!!!!!!')
      review.splice(index, 1);
   }
});
Alaeddin Hussein
źródło
1
Myślę, że tak nie jest. Zmieniłem kod, zakładając, że p jest indeksem, a teraz nawet nie dostaje się do ifinstrukcji.
nowicjusz Prgrmr
3
@WhoCares Powinieneś zobaczyć specyfikację ecma-international.org/ecma-262/5.1/#sec-15.4.4.18 . Argumenty funkcji callBack toitem, index, object
Xotic750