使用 for...of 循环删除数组项

11

我正在尝试编辑一个数组并删除不符合特定条件的元素。如果我使用reverse for循环结合.splice(index, n),代码就可以正常工作。我卡在用ES6的for...of循环实现同样的功能上。

let array=[1,2,3];
//reverse for loop
for(var i=array.length - 1; i>=0; i--) {
  if(array[i]>=2) {
    /*Collect element before deleting it.*/
    array.splice(i,1);
  }
} 
console.log(array);
// returns [1]

使用 for..of

let array=[1,2,3];
for(let entry of array) {
    if(entry>=2) {
        let index = array.indexOf(entry);

        /*The array is being re-indexed when I apply 
        .splice() - the loop will skip over an index
        when one element of array is removed*/

        array.splice(index, 1);
    }
} 
console.log(array);
//returns [1,3]

是否有办法使用for...of循环来实现这个功能?还是说我必须坚持使用反向for循环

更新:
我需要将不符合filter()逆向for循环函数删除的元素收集到一个辅助数组中。


在循环数组时不要使用splice,因为它会改变原始数组。 - Code Maniac
4
非常复杂。使用.filter代替,更容易。 - CertainPerformance
filter 就是你需要的。在迭代数组时改变它是一种不好的做法。 - Florian
如果我使用反向for循环结合.splice(index,n),代码就能正常工作。这是正确的方法。你也可以像这样使用while(链接:https://dev59.com/Smkw5IYBdhLWcg3wdKQv#9882349) - adiga
3个回答

12

您无法合理地使用for-of来完成此操作。如果在迭代过程中删除“当前”条目,则会跳过下一个条目,因为数组迭代器的规定方式(它们使用条目的索引)。您可以通过以下构造的示例来查看:

const array = [1, 2, 3];
for (const entry of array) {
    console.log(entry);
    if (entry === 2) {
        array.splice(1, 1);
    }
}
console.log(array);

注意到entry 3 没有迭代循环。

我建议您继续使用反向的for循环或者使用filter生成一个新的数组,包含您想要保留的条目。

使用filter

let array = [1, 2, 3];
array = array.filter(entry => entry < 2);
console.log(array);

我之前说“合理”是因为,当然总有方法。您可以循环遍历数组的副本并在其外部维护索引


const array = [1, 2, 3];
let index = 0;
for (const entry of [...array]) {
    if (entry >= 2) {
        array.splice(index, 1);
    } else {
        ++index;
    }
}
console.log(array);

与其他选择相比,这似乎不合理,除非当然有推动您这样做的限制。 :-)


我需要在删除数组元素之前执行其他操作。我能否使用“filter”函数实现相同的功能?(请参见更新的问题)。 - danieln
1
@MobeenSarwar - 因为,正如我所说,它们使用索引。请参阅规范 - T.J. Crowder
@T.J.Crowder 我有一个 Node.js 服务器,其中包含一个充当本地缓存的对象数组。我需要在某些事件发生时清理/优化缓存,因此需要检查每个对象的条件,如果对象属性满足特定条件,则将对象持久化到数据库中,否则将对象保留在缓存中。 - danieln
@JuneM3ta - 我认为聊天功能对于与问题相关的讨论并不有用。如果您有后续问题,请发帖提问。如果您对此答案不理解或需要更多信息,请在此处评论。 (是的,我在这方面与SO意见不合。我认为将相关对话推到聊天中是一个错误。) - T.J. Crowder
@JuneM3ta - 过滤器回调函数将为数组中的每个条目调用。同样,您可以像上面所示一样访问数组中的任何内容。 - T.J. Crowder
显示剩余7条评论

5
根据@T.J的回答,在使用for...of循环时:

如果你在迭代期间删除了“current”条目,由于数组迭代器的规定方式(它们使用条目的索引),下一个条目将被跳过。

这留下了另外两个选项,使用反向for循环filter函数。我之前提到过,在删除当前数组元素之前需要对其执行某个操作。
1.使用.filter()函数
并参考@T.J的评论
let array = [1,2,3];
let collection = [];

array = array.filter(function(entry) {
    if(entry>=2) {
        collection.push(entry);
    } 
    return entry <2;
});

console.log(collection); //returns [2,3]
console.log(array); //returns [1]

2. 使用反向for循环

let array = [1,2,3];
let collection = [];

for(var i=array.length - 1; i>=0; i--) {
  if(array[i]>=2) {
     collection.push(array[i]);
     array.splice(i,1);
  }
}

console.log(collection); //returns [2,3]
console.log(array); //returns [1] 

在这种情况下,filter()函数需要一个额外的步骤来暂时保存不符合条件的元素。而反向for循环提供了一种更简洁的方法来达到相同的效果。


我也在考虑这个案例。 - Mobeen Sarwar
你没有提到需要收集你移除的条目(你的第二个解决方案也没有)。 - T.J. Crowder
1
@T.J.Crowder 我已经更新了我的回答。另外,我的重点更多地放在 for...of 循环的操作上。不好意思,我应该早点提到这个!! - danieln

2

在数组中删除当前条目时,迭代器会跳过下一个条目。因此,您应该使用以下方式来实现所需的结果。

最初的回答:iterator skips the next entry on removing current entry in the array. So you should this way to achieve your desired result.

let array = [1,2,3];
let array_new = [1,2,3];
for(let entry of array) {
if(entry>=2) {
    let index = array_new.indexOf(entry);
    array_new.splice(index, 1);
}
} 
console.log(array_new);
//returns [1]

1
在这种情况下,反向for循环提供了一个更加简洁的替代方案 :). 请参见此答案 - danieln
@JuneM3ta 是的,它是。 - Mobeen Sarwar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接