Bagaimana cara menghapus elemen dari array di loop forEach?

97

Saya mencoba untuk menghapus elemen dalam array di a forEach lingkaran, tetapi mengalami masalah dengan solusi standar yang pernah saya lihat.

Inilah yang sedang saya coba:

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

Saya tahu itu masuk ke dalam ifkarena saya melihat YippeeeeeE!!!!!!!!!!!!!di konsol.

MASALAH SAYA: Saya tahu bahwa loop for saya dan jika logikanya terdengar, tetapi upaya saya untuk menghapus elemen saat ini dari array gagal.

MEMPERBARUI:

Mencoba jawaban Xotic750, dan elemennya masih belum dihapus:

Inilah fungsi di kode saya:

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

Berikut adalah output di mana lariknya masih belum dihapus:

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

Jadi jelas akan masuk ke pernyataan if seperti yang diarahkan, tapi juga jelas bahwa [• • •] masih ada.

novicePrgrmr
sumber
8
Apakah ada alasan yang Anda gunakan forEach? Jika Anda ingin menghapus item, fungsi yang paling tepat adalah filter.
Jon
2
Tidak jika Anda perlu menyimpan referensi ke larik asli.
Xotic750
Ya, kami ingin menyimpan referensi ke larik asli.
novicePrgrmr
Tidak jelas dari pertanyaan Anda, apa sebenarnya masalah yang Anda hadapi? Bisakah Anda memberi contoh, mungkin jsFiddle? Tampaknya Anda mungkin harus menggunakan indexatribut daripada itemuntuksplice
Xotic750
@ Xotic750 Maaf, klarifikasi ditambahkan.
novicePrgrmr

Jawaban:

235

Sepertinya Anda mencoba melakukan ini?

Iterasi dan mutasikan array menggunakan 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>

Yang berfungsi dengan baik untuk kasus sederhana di mana Anda tidak memiliki 2 nilai yang sama dengan item array yang berdekatan, jika tidak, Anda memiliki masalah ini.

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>

Jadi apa yang dapat kita lakukan tentang masalah ini saat melakukan iterasi dan mutasi array? Solusi yang biasa dilakukan adalah bekerja secara terbalik. Menggunakan ES3 sementara tapi Anda bisa menggunakan untuk gula jika pilihan

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>

Oke, tetapi Anda ingin menggunakan metode iterasi ES5. Nah dan pilihannya adalah menggunakan Array.prototype.filter tetapi ini tidak mengubah array asli tetapi membuat yang baru, jadi meskipun Anda bisa mendapatkan jawaban yang benar, itu bukan yang Anda tentukan.

Kita juga bisa menggunakan ES5 Array.prototype.reduceRight , bukan untuk properti pereduksi melainkan dengan properti iterasinya, yaitu iterasi secara terbalik.

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>

Atau kita bisa menggunakan ES5 Array.protoype.indexOf seperti itu.

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>

Tetapi Anda secara khusus ingin menggunakan ES5 Array.prototype.forEach , jadi apa yang bisa kami lakukan? Kita perlu menggunakan Array.prototype.slice untuk membuat salinan dangkal dari array dan Array.prototype.reverse sehingga kita dapat bekerja secara terbalik untuk mengubah array asli.

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>

Akhirnya ES6 menawarkan kepada kita beberapa alternatif lebih lanjut, di mana kita tidak perlu membuat salinan dangkal dan membalikkannya. Terutama kita bisa menggunakan Generator dan Iterator . Namun dukungannya cukup rendah saat ini.

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>

Sesuatu yang perlu diperhatikan dalam semua hal di atas adalah, jika Anda menghapus NaN dari array kemudian membandingkan dengan sama dengan tidak akan berfungsi karena di JavascriptNaN === NaN salah. Tapi kita akan mengabaikan itu dalam solusi karena itu kasus edge lain yang tidak ditentukan.

Jadi begitulah, jawaban yang lebih lengkap dengan solusi yang masih memiliki kasus tepi. Contoh kode pertama masih benar tetapi seperti yang dinyatakan, ini bukannya tanpa masalah.

Xotic750
sumber
Terimakasih telah menjawab. Saya mencoba menggunakan solusi Anda, tetapi masih belum menghapus elemen dari array. Saya akan memberikan detail dalam pertanyaan.
novicePrgrmr
Letakkan console.log(review);setelah forEach, seperti dalam contoh saya.
Xotic750
4
Hati-hati, ini rusak jika dua elemen berurutan untuk dihapus: var review = ['a', 'a', 'c', 'b', 'a']; akan menghasilkan ['a', 'c', 'b']
quentinadam
3
CATATAN - Jawaban ini SALAH! The foreach melakukan iterasi melalui larik menurut indeks. Setelah Anda menghapus elemen saat mengulang indeks item berikut berubah. Dalam contoh ini, setelah Anda menghapus 'a' pertama, nomor indeks 1 sekarang menjadi 'c'. Oleh karena itu, 'b' pertama bahkan tidak dievaluasi. Karena Anda tidak mencoba menghapusnya, kebetulan saja baik-baik saja, tetapi bukan itu caranya. Anda harus melakukan iterasi melalui salinan terbalik dari larik, lalu menghapus item dalam larik asli.
danbars
4
@ Xotic750 - jawaban asli (sekarang menjadi cuplikan kode pertama) salah hanya karena forEach tidak akan mengulang melalui semua elemen dalam array, seperti yang saya jelaskan di komentar sebelumnya. Saya tahu bahwa pertanyaannya adalah bagaimana menghapus elemen dalam loop forEach, tetapi jawaban sederhananya adalah Anda tidak melakukan itu. Karena banyak orang membaca jawaban tersebut, dan berkali-kali menyalin jawaban secara membabi buta (terutama jawaban yang diterima), penting untuk mencatat kekurangan dalam kode. Saya pikir reverse while loop adalah solusi yang paling sederhana, paling efisien dan paling mudah dibaca dan harus menjadi jawaban yang diterima
danbars
37

Gunakan Array.prototype.filteralih-alih 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
sumber
10

Meskipun jawaban Xotic750 memberikan beberapa poin bagus dan solusi yang mungkin, terkadang sederhana lebih baik .

Anda tahu bahwa array yang diiterasi sedang bermutasi dalam iterasinya sendiri (yaitu menghapus item => perubahan indeks), jadi logika paling sederhana adalah mundur dengan gaya lama for(bahasa à 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());

Jika Anda benar-benar memikirkannya, a forEachhanyalah gula sintaksis untuk satu forlingkaran ... Jadi jika itu tidak membantu Anda, hentikan saja.

CPHPython
sumber
1

Anda juga dapat menggunakan indexOf untuk melakukan ini

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

Saya mengerti bahwa Anda ingin menghapus dari array menggunakan kondisi dan memiliki array lain yang itemnya telah dihapus dari array. Benar?

Bagaimana dengan ini?

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

Semoga bantuan ini ...

Ngomong-ngomong, saya membandingkan 'for-loop' dengan 'forEach'.

Jika dihapus jika string berisi 'f', hasilnya berbeda.

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

Dan dihilangkan dengan setiap iterasi, juga hasilnya berbeda.

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

Taman Jun-Hong
sumber
0

Berikut ini akan memberi Anda semua elemen yang tidak sama dengan karakter khusus Anda!

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

Inilah cara Anda melakukannya:

review.forEach(function(p,index,object){
   if(review[index] === '\u2022 \u2022 \u2022'){
      console.log('YippeeeE!!!!!!!!!!!!!!!!')
      review.splice(index, 1);
   }
});
Alaeddin Hussein
sumber
1
Saya tidak berpikir demikian. Saya mengubah kode saya dengan asumsi p adalah indeks, dan sekarang bahkan tidak masuk ke ifpernyataan.
novicePrgrmr
3
@WhoCares Anda harus melihat spesifikasi ecma-international.org/ecma-262/5.1/#sec-15.4.4.18 Argumen fungsi callBack adalahitem, index, object
Xotic750