在Javascript / jQuery中从数组中删除多个元素


117

我有两个数组。第一个数组包含一些值,而第二个数组包含应从第一个数组中删除的值的索引。例如:

var valuesArr = new Array("v1","v2","v3","v4","v5");   
var removeValFromIndex = new Array(0,2,4);

我想在指数删除值目前0,2,4valuesArr。我认为本机splice方法可能会有所帮助,所以我想到了:

$.each(removeValFromIndex,function(index,value){
    valuesArr.splice(value,1);
});

但这是行不通的,因为在每个之后splice,中的值的索引valuesArr都不同。我可以通过使用临时数组并将所有值复制到第二个数组来解决此问题,但我想知道是否有任何本机方法可以传递多个索引以从数组中删除值。

我更喜欢jQuery解决方案。(不确定是否可以grep在这里使用)

Answers:


255

总有一个简单的旧for循环:

var valuesArr = ["v1","v2","v3","v4","v5"],
    removeValFromIndex = [0,2,4];    

for (var i = removeValFromIndex.length -1; i >= 0; i--)
   valuesArr.splice(removeValFromIndex[i],1);

经过removeValFromIndex按相反的顺序,你可以.splice()不会弄乱尚未要被移除的内容索引。

请注意,在上文中,我使用了带有方括号的array-literal语法来声明两个数组。这是推荐的语法,因为new Array()使用情况可能会造成混淆,因为它取决于您传入的参数的多少而做出不同的响应。

编辑:刚刚看到您对另一个有关索引数组的答案的评论,不一定按任何特定顺序。如果是这种情况,请在开始之前按降序对其进行排序:

removeValFromIndex.sort(function(a,b){ return b - a; });

并遵循$.each()您喜欢的任何循环//等方法。


1
不会切片乱七八糟的索引
穆罕默德·乌默

5
@MuhammadUmer-不,如果操作正确,则不会,这就是我的回答。
nnnnnn 2013年

5
感谢您对反向订单的了解。
丹尼尔·纳尔巴赫

2
+1,我没有意识到我必须按相反的顺序进行拼接,尽管我的方法不是使用forEach,而是使用$.each(rvm.reverse(), function(e, i ) {})
Luis Stanley Jovel,2016年

1
只有removeValFromIndex 按升序排列
这才起作用

24

这是我不使用lodash / underscore时使用的一种:

while(IndexesToBeRemoved.length) {
    elements.splice(IndexesToBeRemoved.pop(), 1);
}

优雅的解决方案!起初我认为这是行不通的,因为我认为每次调用slice时都必须重新计算要删除的索引(-1上的IndexestoBeRemoved),但是它确实有效!
Renato Gama

2
太聪明了
Farzad YZ

11
如果仅将IndexesToBeRemoved数组按升序排序,则可以使用此解决方案。
xfg '17

这些索引将在第一次拼接后失效。
Shinzou

@shinzou-不是IndexesToBeRemoved排序(升序)。
nnnnnn

18

不能,in-place但是可以使用grepinArray功能来完成jQuery

var arr = $.grep(valuesArr, function(n, i) {
    return $.inArray(i, removeValFromIndex) ==-1;
});

alert(arr);//arr contains V2, V4

检查这个小提琴。


如果您刚刚说过,它将就在(足够近的地方)valuesArr = $.grep(...);
nnnnnn'2

1
@nnnnnn哈哈哈我要去开会(你知道他们让你如此有效率),所以没有做太多的尝试。
TheVillageIdiot 2012年

1
@ cept0为什么要投票?OP要求使用jQuery解决方案。
Ste77

非常感谢!@TheVillageIdiot
ecorvo

jsfiddler-错误404。我们真的很抱歉,但是没有这样的页面。
Ash Ash


7
function filtermethod(element, index, array) {  
    return removeValFromIndex.find(index)
}  
var result = valuesArr.filter(filtermethod);

MDN参考在这里


@ riship89小提琴落下了
mate64

@Karna:小提琴落下了
riship89 2014年

1
请注意,在撰写本文时(2014年6月),Array.prototype.find是当前ES6草案的一部分,仅在当前的firefox中
Olli K

6

在纯JS中,您可以向后循环遍历数组,因此splice()不会弄乱循环中下一个元素的索引:

for (var i = arr.length - 1; i >= 0; i--) {
    if ( yuck(arr[i]) ) {
        arr.splice(i, 1);
    }
}

不起作用yuck不是函数,因此假设它是不需要的元素索引的索引,并且使用yuck [arr [i]]
DavChana

5

感觉有必要发布带有O(n)时间的答案:)。拼接解决方案的问题在于,由于array的底层实现实际上是一个array,因此每次splice调用都将花费O(n)时间。当我们设置一个示例来利用此行为时,这最为明显:

var n = 100
var xs = []
for(var i=0; i<n;i++)
  xs.push(i)
var is = []
for(var i=n/2-1; i>=0;i--)
  is.push(i)

这会从中间开始删除元素,因此每次删除都会迫使js引擎复制n/2元素,我们(n/2)^2总共进行了二次复制操作。

拼接解决方案(假设is已经按照降序排序以摆脱开销)是这样的:

for(var i=0; i<is.length; i++)
  xs.splice(is[i], 1)

但是,通过从头开始重建数组,使用蒙版查看是否复制元素(排序会将其推送到O(n)log(n)),来实现线性时间解决方案并不难。以下是这样的实现(不是mask为了速度而将布尔值反转):

var mask = new Array(xs.length)
for(var i=is.length - 1; i>=0; i--)
  mask[is[i]] = true
var offset = 0
for(var i=0; i<xs.length; i++){
  if(mask[i] === undefined){
    xs[offset] = xs[i]
    offset++
  }
}
xs.length = offset

我在jsperf.com上运行了它,即使n=100是拼接方法也慢了90%。对于较大的n差异将更大。


5

快速ES6一线:

const valuesArr = new Array("v1","v2","v3","v4","v5");   
const removeValFromIndex = new Array(0,2,4);

const arrayWithValuesRemoved = valuesArr.filter((value, i) => removeValFromIndex.includes(i))

如果您removeValFromIndex使用Set()removeValFromIndex.has而不是,则您的代码应该运行得更快includes
鲍里斯

5

使用filterSet的简单有效的(线性复杂度)解决方案:

const valuesArr = ['v1', 'v2', 'v3', 'v4', 'v5'];   
const removeValFromIndex = [0, 2, 4];

const indexSet = new Set(removeValFromIndex);

const arrayWithValuesRemoved = valuesArr.filter((value, i) => !indexSet.has(i));

console.log(arrayWithValuesRemoved);

该实现的最大优点是Set查找操作(has函数)需要固定的时间,例如,比nevace的答案要快。


@MichaelPaccione很高兴提供帮助:)
Alberto Trindade Tavares,

3

这对我来说很好,并且在从对象数组中删除时也可以工作:

var array = [ 
    { id: 1, name: 'bob', faveColor: 'blue' }, 
    { id: 2, name: 'jane', faveColor: 'red' }, 
    { id: 3, name: 'sam', faveColor: 'blue' }
];

// remove people that like blue

array.filter(x => x.faveColor === 'blue').forEach(x => array.splice(array.indexOf(x), 1));

可能有一种更短,更有效的方式编写此代码,但这确实可行。


2

使用ES5的简单解决方案。如今,这似乎更适合大多数应用程序,因为许多应用程序不再希望依赖jQuery等。

当要删除的索引按升序排序时:

var valuesArr = ["v1", "v2", "v3", "v4", "v5"];   
var removeValFromIndex = [0, 2, 4]; // ascending

removeValFromIndex.reverse().forEach(function(index) {
  valuesArr.splice(index, 1);
});

当要删除的索引未排序时:

var valuesArr = ["v1", "v2", "v3", "v4", "v5"];   
var removeValFromIndex = [2, 4, 0];  // unsorted

removeValFromIndex.sort(function(a, b) { return b - a; }).forEach(function(index) {
  valuesArr.splice(index, 1);
});

1

您可以通过替换removeValFromIndex为来更正代码removeValFromIndex.reverse()。如果不能保证该数组使用升序,则可以使用removeValFromIndex.sort(function(a, b) { return b - a })


对我来说很好-jsfiddle.net/mrtsherman/gDcFu/2。尽管这使删除列表井然有序。
mrtsherman '02

@minopret:谢谢,但是只有在索引removeValFromIndex按升序排列时它才起作用。
xyz 2012年


1

如果您使用的是underscore.js,则可以使用_.filter()来解决您的问题。

var valuesArr = new Array("v1","v2","v3","v4","v5");
var removeValFromIndex = new Array(0,2,4);
var filteredArr = _.filter(valuesArr, function(item, index){
                  return !_.contains(removeValFromIndex, index);
                });

此外,如果您尝试使用项目列表而不是索引来删除项目,则可以简单地使用_.without(),如下所示:

var valuesArr = new Array("v1","v2","v3","v4","v5");
var filteredArr = _.without(valuesArr, "V1", "V3");

现在filteredArr应该是["V2", "V4", "V5"]


如何在下划线中实现包含...如果它等同于过滤器内部的indexOf,请务必小心...根本不是最佳选择...
Alvaro Joao

1

过滤器 + indexOf(IE9 +):

function removeMany(array, indexes) {
  return array.filter(function(_, idx) {
    return indexes.indexOf(idx) === -1;
  });
}); 

或使用ES6 过滤器 + 查找(Edge +):

function removeMany(array, indexes = []) {
  return array.filter((_, idx) => indexes.indexOf(idx) === -1)
}

indexOf过滤器内部...不是最佳
Alvaro Joao

1

这是一个快捷方式。

function removeFromArray(arr, toRemove){
    return arr.filter(item => toRemove.indexOf(item) === -1)
}

const arr1 = [1, 2, 3, 4, 5, 6, 7]
const arr2 = removeFromArray(arr1, [2, 4, 6]) // [1,3,5,7]

indexOf过滤器内部...不是最佳
Alvaro Joao

0

听起来像Apply(应用)可能是您想要的。
也许像这样的事情会起作用吗?

Array.prototype.splice.apply(valuesArray, removeValFromIndexes );

但是该.splice()方法不希望删除元素列表,而是希望从该元素的单个索引开始删除,然后删除元素的数量...
nnnnnn 2012年

0

对于多个项目或唯一项目:

我建议你用 Array.prototype.filter

如果您已经知道索引,则永远不要使用indexOf !:

var valuesArr = ["v1","v2","v3","v4","v5"];
var removeValFrom = [0, 2, 4];

valuesArr = valuesArr.filter(function(value, index) {
     return removeValFrom.indexOf(index) == -1;
}); // BIG O(N*m) where N is length of valuesArr and m is length removeValFrom

做:

使用哈希...使用Array.prototype.map

  var valuesArr = ["v1","v2","v3","v4","v5"];
  var removeValFrom = {};
  ([0, 2, 4]).map(x=>removeValFrom[x]=1); //bild the hash.
  valuesArr = valuesArr.filter(function(value, index) {
      return removeValFrom[index] == 1;
  }); // BIG O(N) where N is valuesArr;

0
var valuesArr = new Array("v1","v2","v3","v4","v5");   
var removeValFromIndex = new Array(0,2,4);

console.log(valuesArr)
let arr2 = [];

for (let i = 0; i < valuesArr.length; i++){
  if (    //could also just imput this below instead of index value
    valuesArr[i] !== valuesArr[0] && // "v1" <--
    valuesArr[i] !== valuesArr[2] && // "v3" <--
    valuesArr[i] !== valuesArr[4]    // "v5" <--
  ){
    arr2.push(valuesArr[i]);
  }
}

console.log(arr2);

这可行。但是,您将在此过程中创建一个新阵列。不知道那是否是您想要的,但是从技术上讲,它将是一个仅包含您想要的值的数组。


-1

您可以尝试使用delete array[index]这样做不会完全删除元素,而是将值设置为undefined


谢谢,但我想删除元素。
xyz 2012年

-1

您可以Set从数组构造一个,然后从该集合创建一个数组。

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.