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

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

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

我想从 valuesArr中移除指数 0,2,4处的值。我认为原生 splice方法可能会有所帮助,所以我想出了:

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

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

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

249130 次浏览

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

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

MDN 参考是 给你

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

听起来像 申请可能是你正在寻找的。
也许这样能行?

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

总是有一个简单的老式 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()/etc 方法执行该操作。

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

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


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

检查 这个小提琴。

如果您正在使用 下划线 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"]

这里有一种可能性:

valuesArr = removeValFromIndex.reduceRight(function (arr, it) {
arr.splice(it, 1);
return arr;
}, valuesArr.sort(function (a, b) { return b - a }));

JsFiddle 上的示例

MDN 在 Array.Prototype.reduceRight 上

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

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

感觉有必要在 O(n)时间发布一个答案:)。拼接解决方案的问题在于,由于数组的底层实现是 字面意思是一个数组,因此每个 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)

然而,实现一个线性时间解决方案并不困难,方法是从头开始重新构建数组,使用一个掩码来查看是否复制元素(sort 会将其推送到 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,这种差异将大得多。

下面是我在不使用 loash/下划线时使用的一种方法:

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

过滤器 + 索引(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)
}

使用 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);
});

我建议你用 数组,原型,过滤器

var valuesArr = ["v1","v2","v3","v4","v5"];
var removeValFrom = [0, 2, 4];
valuesArr = valuesArr.filter(function(value, index) {
return removeValFrom.indexOf(index) == -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,如果你已经知道了索引! :

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

做:

使用 Hash... 使用 数组,原型,地图

  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;

快速 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))
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);

这个管用。但是,您将在此过程中创建一个新数组。不确定您是否希望这样,但从技术上讲,它将是一个只包含您想要的值的数组。

使用 过滤器预备的简单而有效的(线性复杂度)解决方案:

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 的回答要快,例如。

这对我来说很有效,在从对象数组中删除时也很有效:

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));

可能有一个更短的更有效的方式来写这个,但这确实工作。

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

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
removeValFromIndex.forEach(function(toRemoveIndex){
valuesArr.splice(toRemoveIndex,1);
});

您可以尝试 Lodashjs 库函数(_.forEach()_.remove())。我正在使用这种技术从表中删除多个行。

let valuesArr = [
{id: 1, name: "dog"},
{id: 2, name: "cat"},
{id: 3, name: "rat"},
{id: 4, name: "bat"},
{id: 5, name: "pig"},
];
let removeValFromIndex = [
{id: 2, name: "cat"},
{id: 5, name: "pig"},
];
_.forEach(removeValFromIndex, (indi) => {
_.remove(valuesArr, (item) => {
return item.id === indi.id;
});
})
console.log(valuesArr)
/*[
{id: 1, name: "dog"},
{id: 3, name: "rat"},
{id: 4, name: "bat"},
];*/

在变异数组之前,不要忘记克隆(_.clone(valuesArr)[...valuesArr])

试试这个

var valuesArr = new Array("v1", "v2", "v3", "v4", "v5");
console.info("Before valuesArr = " + valuesArr);
var removeValFromIndex = new Array(0, 2, 4);
valuesArr = valuesArr.filter((val, index) => {
return !removeValFromIndex.includes(index);
})
console.info("After valuesArr = " + valuesArr);

我认为这是最优雅的解决方案:

const oldArray = [1, 2, 3, 4, 5]
const removeItems = [1, 3, 5]


const newArray = oldArray.filter((value) => {
return !removeItems.includes(value)
})


console.log(newArray)

产出:

[2, 4]

甚至更短:

const newArray = oldArray.filter(v => !removeItems.includes(v))