จะลบองค์ประกอบออกจากอาร์เรย์ใน forEach loop ได้อย่างไร?


103

ฉันกำลังพยายามลบองค์ประกอบในอาร์เรย์แบบforEachวนซ้ำ แต่มีปัญหากับโซลูชันมาตรฐานที่ฉันเคยเห็น

นี่คือสิ่งที่ฉันกำลังพยายาม:

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

ฉันรู้ว่ามันเข้ามาifเพราะฉันเห็นYippeeeeeE!!!!!!!!!!!!!ในคอนโซล

ปัญหาของฉัน:ฉันรู้ว่าลอจิกสำหรับลูปและถ้าตรรกะของฉันฟังดูดี แต่ความพยายามของฉันในการลบองค์ประกอบปัจจุบันออกจากอาร์เรย์ล้มเหลว

อัพเดท:

ลองใช้คำตอบของ Xotic750 แล้วองค์ประกอบยังไม่ถูกลบ:

นี่คือฟังก์ชันในรหัสของฉัน:

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

นี่คือผลลัพธ์ที่อาร์เรย์ยังไม่ถูกลบ:

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

เห็นได้ชัดว่ามันจะเข้าสู่คำสั่ง if ตามที่กำกับไว้ แต่ก็เห็นได้ชัดว่า [•••] ยังคงอยู่ที่นั่น


8
มีเหตุผลที่คุณใช้forEachหรือไม่? filterหากคุณต้องการที่จะลบรายการฟังก์ชั่นที่เหมาะสมที่สุดคือ
จอน

2
ไม่ใช่หากคุณต้องการเก็บข้อมูลอ้างอิงไปยังอาร์เรย์เดิม
Xotic750

ใช่เราต้องการเก็บข้อมูลอ้างอิงไปยังอาร์เรย์เดิม
novoprgrmr

ไม่ชัดเจนจากคำถามของคุณปัญหาที่แท้จริงที่คุณกำลังประสบคืออะไร? คุณช่วยยกตัวอย่างได้ไหมอาจจะเป็น jsFiddle ดูเหมือนว่าคุณน่าจะใช้indexแอตทริบิวต์มากกว่าitemสำหรับsplice
Xotic750

@ Xotic750 ขออภัยเพิ่มคำชี้แจง
novoprgrmr

คำตอบ:


246

ดูเหมือนว่าคุณกำลังพยายามทำสิ่งนี้?

ทำซ้ำและกลายพันธุ์อาร์เรย์โดยใช้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>

ซึ่งใช้งานได้ดีสำหรับกรณีง่ายๆที่คุณไม่มี 2 ค่าเดียวกันกับรายการอาร์เรย์ที่อยู่ติดกันอีกทางหนึ่งที่คุณมีปัญหานี้

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>

เราจะทำอย่างไรกับปัญหานี้เมื่อทำซ้ำและกลายพันธุ์อาร์เรย์ วิธีแก้ปัญหาตามปกติคือการทำงานในสิ่งที่ตรงกันข้าม ใช้ ES3 ในขณะที่แต่คุณสามารถใช้สำหรับน้ำตาลได้หากต้องการ

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>

โอเค แต่คุณต้องการใช้วิธีการทำซ้ำ ES5 ดีและตัวเลือกคือการใช้Array.prototype.filterแต่จะไม่กลายพันธุ์อาร์เรย์เดิม แต่สร้างใหม่ดังนั้นในขณะที่คุณจะได้รับคำตอบที่ถูกต้อง แต่ก็ไม่ใช่สิ่งที่คุณระบุไว้

นอกจากนี้เรายังสามารถใช้ ES5 Array.prototype.reduceRightไม่ใช่สำหรับคุณสมบัติการลดโดยใช้คุณสมบัติการวนซ้ำเช่นวนซ้ำในสิ่งที่ตรงกันข้าม

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>

หรือเราจะใช้ ES5 Array.protoype.indexOfก็ได้

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>

แต่คุณต้องการใช้ ES5 Array.prototype.forEachโดยเฉพาะเราจะทำอย่างไร เราต้องใช้Array.prototype.sliceเพื่อสร้างสำเนาตื้น ๆ ของอาร์เรย์และArray.prototype.reverseเพื่อให้เราสามารถทำงานในสิ่งที่ตรงกันข้ามเพื่อกลายพันธุ์อาร์เรย์เดิม

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>

ในที่สุด ES6 ก็เสนอทางเลือกเพิ่มเติมให้กับเราโดยที่เราไม่จำเป็นต้องทำสำเนาตื้น ๆ และย้อนกลับ โดยเฉพาะอย่างยิ่งเราสามารถใช้เครื่องปั่นไฟและ Iterators อย่างไรก็ตามการสนับสนุนค่อนข้างต่ำในปัจจุบัน

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>

สิ่งที่ควรทราบในทั้งหมดข้างต้นคือถ้าคุณลอกNaNออกจากอาร์เรย์แล้วการเปรียบเทียบกับเท่ากับจะไม่ทำงานเพราะใน JavascriptNaN === NaNเป็นเท็จ แต่เราจะเพิกเฉยต่อสิ่งนั้นในการแก้ปัญหาเนื่องจากเป็นอีกกรณีขอบที่ไม่ได้ระบุ

ดังนั้นเราจึงมีคำตอบที่สมบูรณ์ยิ่งขึ้นพร้อมโซลูชันที่ยังคงมีขอบเคส ตัวอย่างโค้ดแรกยังคงถูกต้อง แต่ตามที่ระบุไว้มันไม่ได้ไม่มีปัญหา


ขอบคุณสำหรับคำตอบ. ฉันลองใช้โซลูชันของคุณแล้ว แต่ก็ยังไม่ได้ลบองค์ประกอบออกจากอาร์เรย์ ฉันจะใส่รายละเอียดในคำถาม
novoprgrmr

ใส่console.log(review);หลังforEachเช่นในตัวอย่างของฉัน
Xotic750

4
ระวังสิ่งนี้จะแตกหากสององค์ประกอบต่อเนื่องที่จะลบ: var review = ['a', 'a', 'c', 'b', 'a']; จะให้ผล ['a', 'c', 'b']
quentinadam

4
หมายเหตุ - คำตอบนี้ผิด! foreach วนซ้ำผ่านอาร์เรย์โดยดัชนี เมื่อคุณลบองค์ประกอบในขณะที่ทำซ้ำดัชนีของรายการต่อไปนี้จะเปลี่ยนแปลง ในตัวอย่างนี้เมื่อคุณลบ "a" ตัวแรกดัชนีหมายเลข 1 จะกลายเป็น "c" ดังนั้น 'b' ตัวแรกจึงไม่ได้รับการประเมินด้วยซ้ำ เนื่องจากคุณไม่ได้พยายามลบมันมันก็โอเค แต่นั่นไม่ใช่วิธี คุณควรวนซ้ำผ่านสำเนาย้อนกลับของอาร์เรย์จากนั้นลบรายการในอาร์เรย์เดิม
danbars

4
@ Xotic750 - คำตอบเดิม (ตอนนี้เป็นข้อมูลโค้ดตัวแรก) ผิดเพียงเพราะ forEach จะไม่วนซ้ำองค์ประกอบทั้งหมดในอาร์เรย์ตามที่ฉันอธิบายไว้ในความคิดเห็นก่อนหน้า ฉันรู้ว่าคำถามคือวิธีลบองค์ประกอบใน forEach loop แต่คำตอบง่ายๆก็คือคุณไม่ทำเช่นนั้น เนื่องจากหลาย ๆ คนกำลังอ่านคำตอบเหล่านั้นและหลาย ๆ ครั้งที่คัดลอกคำตอบแบบสุ่มสี่สุ่มห้า (โดยเฉพาะคำตอบที่ได้รับการยอมรับ) จึงเป็นสิ่งสำคัญที่จะต้องสังเกตข้อบกพร่องในรหัส ฉันคิดว่าการย้อนกลับในขณะที่วนซ้ำเป็นวิธีที่ง่ายที่สุดมีประสิทธิภาพมากที่สุดและอ่านได้ง่ายที่สุดและควรเป็นคำตอบที่ยอมรับ
danbars

37

ใช้Array.prototype.filterแทน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);

13

แม้ว่าคำตอบของ Xotic750จะให้ประเด็นที่ดีและวิธีแก้ปัญหาที่เป็นไปได้หลายประการ แต่บางครั้งก็ง่ายกว่าง่ายจะดีกว่า

คุณทราบดีว่าอาร์เรย์ที่กำลังทำซ้ำกำลังถูกกลายพันธุ์ในการทำซ้ำ (เช่นการลบรายการ => การเปลี่ยนแปลงดัชนี) ดังนั้นตรรกะที่ง่ายที่สุดคือการย้อนกลับไปในรูปแบบเก่าfor( ภาษาà 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());

ถ้าคุณคิดเกี่ยวกับมันจริงๆ a forEachเป็นเพียงแค่น้ำตาลในการสังเคราะห์สำหรับการforวนซ้ำ ... ดังนั้นถ้ามันไม่ได้ช่วยคุณได้โปรดหยุดหักหัวของคุณกับมัน


1

คุณยังสามารถใช้ indexOf แทนเพื่อทำสิ่งนี้ได้

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

1

ฉันเข้าใจว่าคุณต้องการลบออกจากอาร์เรย์โดยใช้เงื่อนไขและมีอาร์เรย์อื่นที่นำรายการออกจากอาร์เรย์ อยู่ใช่ไหม?

แล้วเรื่องนี้ล่ะ?

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

หวังว่านี่จะช่วยได้ ...

อย่างไรก็ตามฉันเปรียบเทียบ 'for-loop' กับ 'forEach'

หากลบออกในกรณีที่สตริงมี "f" ผลลัพธ์จะแตกต่างกัน

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

และลบโดยการวนซ้ำแต่ละครั้งผลลัพธ์ก็แตกต่างกัน

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

ต่อไปนี้จะให้องค์ประกอบทั้งหมดที่ไม่เท่ากับอักขระพิเศษของคุณ!

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

0

นี่คือวิธีที่คุณควรทำ:

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

1
ฉันไม่คิดว่าเป็นเช่นนั้น ฉันเปลี่ยนรหัสโดยสมมติว่า p เป็นดัชนีและตอนนี้ยังไม่ได้เข้าสู่ifคำสั่ง
novoprgrmr

3
@WhoCares คุณควรเห็นข้อมูลจำเพาะecma-international.org/ecma-262/5.1/#sec-15.4.4.18อาร์กิวเมนต์ของฟังก์ชัน callBack คือitem, index, object
Xotic750
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.