JavaScript - 根据条件删除数组元素

138
我想知道如何在Javascript中实现一种方法,该方法可删除所有满足特定条件的数组元素(最好不使用jQuery)。
例如:
ar = [ 1, 2, 3, 4 ];
ar.removeIf( function(item, idx) {
    return item > 3;
});
上面的代码会遍历数组中的每个项,并删除符合条件(在示例中为item > 3)的所有项。
我刚开始学习javascript,想知道是否有一种简洁高效的方法来完成此操作。
--更新--
如果条件也可以用于对象属性的话,那就太好了。例如:
ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
ar.removeIf( function(item, idx) {
    return item.str == "c";
});

如果item.str == "c",则该项将被删除。

--更新2--

如果索引条件也能生效就太好了。

例如:

ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
ar.removeIf( function(item, idx) {
    return idx == 2;
});
11个回答

181
您可以使用 数组的 filter 方法
代码如下:

ar = [1, 2, 3, 4];
ar = ar.filter(item => !(item > 3));
console.log(ar) // [1, 2, 3]


105
如果你把!(item > 3)翻译成人类语言,它将是item <= 3 - Miha_x64
14
请注意,Array.filter方法会创建一个新的数组。如果你将原始数组引用给另一个变量,那么之后这个变量所引用的内容将不再和原数组相同:ar1 = [1, 2, 3, 4]; ar2 = ar1; ar1 = ar1.filter(item => !(item > 3)); console.log(ar1); console.log(ar2); - Atilla Baspinar
8
在JavaScript中,'!(item > 3)'并不完全等同于'item <= 3'。如果该数组包含未定义的项,那么'!(item > 3)'将为true,而'item <= 3'将为false。 - Rudey
@Rudey,好发现! - Miha_x64
2
这不会从数组中删除项目。它会生成一个新的数组并分配给ar变量。如果任何其他变量已经引用了ar中的初始数组,则它们不会被更新/影响。我不认为这是一个答案。 - alaboudi

68

如果 filter 方法不适用于您的情况,您可以向 Array 添加自己的方法来执行类似的操作。

Array.prototype.removeIf = function(callback) {
    var i = 0;
    while (i < this.length) {
        if (callback(this[i], i)) {
            this.splice(i, 1);
        }
        else {
            ++i;
        }
    }
};

对我来说,这是JavaScript最酷的功能之一。Ian指出了一种更有效的方法来完成同样的事情。考虑到它是JavaScript,每一点都有帮助:

Array.prototype.removeIf = function(callback) {
    var i = this.length;
    while (i--) {
        if (callback(this[i], i)) {
            this.splice(i, 1);
        }
    }
};

这样做可以避免担心更新length或捕获下一项,因为您是从左向右工作而不是从右向左。


谢谢,运行得很好。我喜欢它在迭代过程中考虑了通过切片进行的索引更改。 - dk123
4
有点混乱。请尝试使用 http://jsfiddle.net/n8JEy/4/ - 并将 i 作为第二个参数传递给回调函数。 - Ian
@Ian 我倾向于可读性更好的解决方案,但你的解决方案肯定更有效率。我还将索引作为第二个参数添加了进去(最初只是忽略了它,因为它没有被使用,而且我认为他不需要它)。 - pickypg
作为 JavaScript 的新手,您可以自由控制此类回调函数的参数,并且只要信息可用,就可以添加任何您想要的内容(例如,如果您有一些倾向,可以使用 this.length 作为第三个参数添加当前数组的长度)。 - pickypg
1
修改预定义类的原型是有风险的。应该只以 polyfill 的方式进行,即添加已在规范中声明但尚未被浏览器实现的成员。感谢您提供的解决方案,我将其用作本地函数。 - Miha_x64
显示剩余2条评论

46
你可以使用 Array.filter(),它的功能与此相反。
ar.filter(function(item, idx) {
    return item <= 3;
});

2
提醒一下,该功能不支持IE8或更低版本。您还应该否定原始表达式,或者它应该是<= - pickypg
24
Array.filter() 方法返回一个新的数组,其包含符合指定条件的原始数组中的所有元素。如果需要从原始数组中移除元素而不是创建新的数组,则可以使用 Array.splice() 方法或者在循环中使用 Array.splice() 或 delete 运算符来实现。 - dk123
使用 Array.filter 创建新数组通常比手动从给定数组中剪切更快。 - Casey Chu
1
@Blender 非常有趣。我发誓最近刚研究过两者之间的比较,结果却相反了。我刚才说的愚蠢评论已经删除了。感谢您提供证明 :) - Ian
@Ian:我只测试了Chrome,所以对于其他浏览器你说的也许是对的。 - Blender
显示剩余4条评论

9
你可以使用 lodash.remove
var array = [1, 2, 3, 4];
var evens = _.remove(array, function(n) {
  return n % 2 == 0;
});

console.log(array);
// => [1, 3]

console.log(evens);
// => [2, 4]

9

使用箭头函数将其转化为一行代码:

ar = ar.filter(i => i > 3);

4

如果您需要删除一个项目,且确定该项目存在,则可以使用以下一行代码:

ar.splice(ar.findIndex(el => el.id === ID_TO_REMOVE), 1);

// or with custom method:
let ar = [ {id:1, str:"a"}, {id:2, str:"b"}, {id:3, str:"c"}, {id:4,str:"d"} ];
ar.removeById = id => ar.splice(ar.findIndex(el => el.id === id), 1);
ar.removeById(ID_TO_REMOVE);

http://jsfiddle.net/oriadam/72kgprw5/

只使用ES6语法


splice() + findIndex() 不比仅使用 filter() 过滤掉已删除的值更快。除非在某些情况下有所不同? - Saturn K
1
这里的失败在于将最后一个数组项剪切掉了,索引为-1。"如果你确定它存在"是一个错误的前提。 - StayCool

4

我对数字数组的解决方案是:

ar = ar.filter(item => item < 4);

最佳答案在这里,简单而优雅。 - Marcel
1
@Marcel,如果您不介意失去对原始数组的引用,那么这是一个很好的答案,如此评论所述:https://dev59.com/EmQo5IYBdhLWcg3whPki#KbmkEYcBWogLw_1bYJWs问题没有重新分配,这意味着需要一种操作原始数组的解决方案。 - Holf

4

如果条件可以在对象属性上起作用,可以简单地编写以下示例:

var ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
var newArray = [];
for (var i = 0, len = ar.length; i<len; i++) {
        if (ar[i].str == "b") 
        {newArray.push(ar[i]);};
 };
console.log(newArray);

See the example Live Example


3

我喜欢这类问题,这里还有我的另一个版本... :)

Array.prototype.removeIf = function(expression) {
   var res = [];
    for(var idx=0; idx<this.length; idx++)
    {
      var currentItem = this[idx];
        if(!expression(currentItem))
        {
            res.push(currentItem);
        }
    }
    return res;
}

ar = [ 1, 2, 3, 4 ];
var result = ar.removeIf(expCallBack);


console.log(result);
function expCallBack(item)
{
    return item > 3;
}

1
你是否可以修改(或添加另一种方法)你的代码来实际删除元素而不是返回一个新数组?感谢你的回复。 - dk123
3
谁曾给这个回答点踩太不对了。这个回答比其他所有回答都要好。请查看@Blender的测试结果jsperf.com/splice-vs-filter,它至少快2倍,在最新版本的Firefox中更快达15倍。 - parliament

1

对于原地删除,我的解决方案是

ar.filter(item => !(item > 3))
  .forEach(obsoleteItem => ar.splice(ar.indexOf(obsoleteItem), 1));

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