如何在JavaScript中遍历数组并删除元素

85

我有一个元素数组,需要从中删除某些元素。问题是JavaScript似乎没有for each循环,如果我使用for循环,就会遇到问题,因为它基本上尝试检查数组边界之外的元素,或者由于索引更改而缺少数组中的元素。让我给你展示一下:

var elements = [1, 5, 5, 3, 5, 2, 4];
for(var i = 0; i < elements.length; i++){
    if(elements[i] == 5){
        elements.splice(i, 1);
    }
}

问题在于当删除元素 elements[1] 后,elements[2] 就成为了新的 elements[1]。因此第一个问题是有些元素从未被检查过。另一个问题是 .length 改变,如果我硬编码边界,那么可能会尝试检查超出数组边界的元素。那么做这件非常简单的事情的最佳方法是什么?


1
elements.splice(i--, 1); - Dagg Nabbit
我不喜欢"--"或"++"的语法,但当从数组开头开始+1时,这是一个有用的提示。 - Xotic750
6个回答

200

从顶部开始!

var elements = [1, 5, 5, 3, 5, 2, 4];
for(var i = elements.length - 1; i >= 0; i--){
    if(elements[i] == 5){
        elements.splice(i, 1);
    }
}

1
从结尾开始计算,避免由于OP提到的长度变化而跳过元素。 - Xotic750
13
for (var i = elements.length; i--;) 可能是更常见的写法。 - Dagg Nabbit
2
我希望我可以多次点赞。Occam从宇宙深处向你发送高能的拍手。 - ingernet
6
真的吗?一个O(n^2)的答案,有147个赞和没有反对?这里有一个踩给你。 - Don Hatch
1
这不就是O(n)吗?它只需要遍历整个数组一次。 - Tasik
显示剩余8条评论

38

您可以在这里使用filter方法:

var elements = [1, 5, 5, 3, 5, 2, 4].filter(function(a){return a !== 5;});
//=> elements now [1,3,2,4]

或者如果您不想触碰元素

var elementsfiltered
   ,elements = [1, 5, 5, 3, 5, 2, 4]
                .filter( function(a){if (a!==5) this.push(a); return true;},
                         elementsfiltered = [] );
   //=> elementsfiltered = [1,3,2,4], elements = [1, 5, 5, 3, 5, 2, 4]

请查看MDN文档以获取filter的相关信息。

或者你可以扩展Array.prototype.

Array.prototype.remove = Array.prototype.remove || function(val){
    var i = this.length;
    while(i--){
        if (this[i] === val){
            this.splice(i,1);
        }
    }
};
var elements = [1, 5, 5, 3, 5, 2, 4];
elements.remove(5);
//=> elements now [1,3,2,4]

1
在创建新数组时,使用哪种过滤器作为解决方案并不是一个坏建议,但实际上OP确实询问如何内联删除元素,因此最好给出一个示例。 - Xotic750
1
似乎是与原始代码无关的不必要变更。OP可以通过在切片后递减i来保留现有的代码。 - Dagg Nabbit
就个人而言,我觉得使用过滤器更安全,因为递减i可能会导致边缘情况,例如如果没有元素等。 - sktguha

10

var elements = [1, 5, 5, 3, 5, 2, 4];    
var i = elements.length;
while (i--) {
    if (elements[i] == 5) {
        elements.splice(i, 1);
    }
}
console.log(elements);


3
就代码可读性而言,这个答案非常好。 - Nico Westerdale

3

每当您删除一个项目时,只需递减i

var elements = [1, 5, 5, 3, 5, 2, 4];

var l = elements.length;
for(var i = 0; i < l; i++){
    if(elements[i] == 5){
        elements.splice(i, 1);
        i--;
    }
}

console.log(elements);


1
使用 Array.shift()
var array = [1, 2, 3, 'a', 'b', 'c'];
while (array.length > 0) {
  console.log(array.shift());
}

编辑:可能不适合规格。我误读了问题(仅删除特定元素),太急于添加尚未提到的方法...


3
正如你在编辑中所说的那样,这并没有真正回答问题,但它确实回答了我想要搜索的问题。谢谢。 - spikyjt

0

这是使用 Array.indexOf, whileArray.splice 在行内删除元素的示例。

var elements = [1, 5, 5, 3, 5, 2, 4];
var remove = 5;
var index = elements.indexOf(remove);

while (index !== -1) {
    elements.splice(index, 1);
    index = elements.indexOf(remove);
}

console.log(elements);

jsfiddle


2
因为O(n^2)算法效率低下而被踩了。 - Alnitak
处理变化的数组长度的规范方法是从数组末尾开始向后处理。或者,也可以从当前位置向前处理,如果已删除当前元素,则不进行增量。相反,您的方法每次找到匹配项时都从第零个元素重新开始,因此会一遍又一遍地重复相同的元素。这是一个非常糟糕的算法,不应该被使用。 - Alnitak
如果您在.indexOf()中使用fromIndex参数,那么我就没有任何抱怨了——这将使其为 O(n)而不是 O(n^2)。目前它只因为使用已编译的内置程序来扫描数组而胜出(对于短数组)。事实上,如果您这样做,您实际上会得到最好的答案。 - Alnitak
1
好的,这很奇怪 - 我尝试了一下(在这个小样本集上),但速度明显变慢了。我还不知道原因。http://jsperf.com/soq-iterate-over-an-array-and-remove/2 - Alnitak
1
我稍微修改了测试用例 - 现在fromIndex的情况运行速度略微更快... http://jsperf.com/soq-iterate-over-an-array-and-remove/3 - Alnitak
显示剩余11条评论

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