使用forEach时splice无法删除元素?技术问题求助
问题:删除匹配LineId的退款项时代码未达预期效果
我想要实现一个逻辑:当products中对象的LineId与refunds中对象的LineId匹配时,给对应商品添加退款状态,同时从refunds里删除这个匹配的对象。最终refunds应该只保留{ Lines: [ {LineId: "444"} ] },但当前代码没有达成预期效果,请问我哪里出错了?
我的代码如下:
products = { Items: [{ Name: "Item Name 1", LineId: "111", Status: [], }, { Name: "Item Name 2", LineId: "222", Status: [], }, { Name: "Item Name 3", LineId: "333", Status: [], } ] } refunds = { Lines: [ {LineId: "222"}, {LineId: "111"}, {LineId: "444"} ] } refunds.Lines.forEach((refundItem, refundIndex) => { console.log("Checking Id " + refundItem.LineId); products.Items.forEach((Item) => { if (refundItem.LineId == Item.LineId) { Item.Status.push({Name: "Refunded"}); //Delete object from refund refunds.Lines.splice(refundIndex, 1); } }); }); console.log(refunds); console.log(products);
问题原因
核心问题出在你在forEach循环中直接用splice修改原数组。forEach是按顺序遍历数组索引的,当你删除某个索引的元素后,数组后面的元素会自动往前移一位,导致下一次循环跳过了原本该处理的元素。
举个实际的执行流程例子:
- 初始
refunds.Lines是[{LineId: "222"}, {LineId: "111"}, {LineId: "444"}] - 第一次循环处理索引0的
222,匹配后删除它,数组变成[{LineId: "111"}, {LineId: "444"}] - 第二次循环直接处理索引1,此时取到的是
444,完全跳过了现在在索引0的111 - 最终
111没有被删除,导致结果不符合预期
解决方案
方法1:使用filter(推荐,逻辑更清晰)
filter会返回一个新数组,不会干扰原数组的遍历顺序,同时可以顺便完成给商品添加状态的操作:
products = { Items: [{ Name: "Item Name 1", LineId: "111", Status: [], }, { Name: "Item Name 2", LineId: "222", Status: [], }, { Name: "Item Name 3", LineId: "333", Status: [], } ] } refunds = { Lines: [ {LineId: "222"}, {LineId: "111"}, {LineId: "444"} ] } // 先把所有商品的LineId收集到一个数组里,方便快速判断匹配 const productLineIds = products.Items.map(item => item.LineId); // 过滤退款项,只保留不在商品LineId列表里的项 refunds.Lines = refunds.Lines.filter(refundItem => { const isMatched = productLineIds.includes(refundItem.LineId); if (isMatched) { // 找到对应商品,添加退款状态 const targetProduct = products.Items.find(item => item.LineId === refundItem.LineId); targetProduct?.Status.push({Name: "Refunded"}); } // 返回false就会被过滤掉,留下不匹配的项 return !isMatched; }); console.log(refunds); // 输出 { Lines: [ {LineId: "444"} ] } console.log(products); // 对应商品的Status已添加Refunded
方法2:倒序遍历数组
如果一定要用循环修改原数组,可以倒序遍历,这样删除元素不会影响前面未处理的索引:
products = { Items: [{ Name: "Item Name 1", LineId: "111", Status: [], }, { Name: "Item Name 2", LineId: "222", Status: [], }, { Name: "Item Name 3", LineId: "333", Status: [], } ] } refunds = { Lines: [ {LineId: "222"}, {LineId: "111"}, {LineId: "444"} ] } // 从最后一个元素往前遍历,删除元素不会干扰未处理的索引 for (let refundIndex = refunds.Lines.length - 1; refundIndex >= 0; refundIndex--) { const refundItem = refunds.Lines[refundIndex]; console.log("Checking Id " + refundItem.LineId); products.Items.forEach(item => { if (refundItem.LineId === item.LineId) { item.Status.push({Name: "Refunded"}); refunds.Lines.splice(refundIndex, 1); } }); } console.log(refunds); // 输出 { Lines: [ {LineId: "444"} ] } console.log(products);
内容的提问来源于stack exchange,提问作者user88432
相关产品推荐
相关产品推荐

