在Javascript / jQuery中从数组中删除多个元素
我有两个数组。 第一个数组包含一些值,而第二个数组包含应该从第一个数组中删除的值的索引。 例如:
var valuesArr = new Array("v1","v2","v3","v4","v5"); var removeValFromIndex = new Array(0,2,4);
我想从valuesArr
删除索引0,2,4
处的valuesArr
。 我认为本地splice
方法可能会有帮助,所以我想出了:
$.each(removeValFromIndex,function(index,value){ valuesArr.splice(value,1); });
但是它不起作用,因为在每次splice
之后, valuesArr
中的值的valuesArr
是不同的。 我可以通过使用临时数组并将所有值复制到第二个数组来解决此问题,但我想知道是否有任何本机方法,我们可以传递多个索引,从中删除数组中的值。
我更喜欢jQuery解决scheme。 (不知道我是否可以在这里使用grep
)
总是有一个简单的老循环:
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()
而不会搞乱尚未被删除的项目的索引。
注意在上面我用方括号的数组字面值语法来声明两个数组。 这是推荐的语法,因为new Array()
使用可能会引起混淆,因为它根据传入的参数有多less响应。
编辑 :只是看到你的评论关于索引数组的另一个答案不一定是在任何特定的顺序。 如果是这种情况,请在开始之前按降序sorting:
removeValFromIndex.sort(function(a,b){ return b - a; });
并按照你喜欢的任何循环/ $.each()
/等方法。
不在in-place
但可以使用jQuery
grep
和inArray
函数来完成。
var arr = $.grep(valuesArr, function(n, i) { return $.inArray(i, removeValFromIndex) ==-1; }); alert(arr);//arr contains V2, V4
检查这个小提琴。
这里是我不使用lodash /下划线时使用的一个:
while(IndexesToBeRemoved.length) { elements.splice(IndexesToBeRemoved.pop(), 1); }
function filtermethod(element, index, array) { return removeValFromIndex.find(index) } var result = valuesArr.filter(filtermethod);
MDN的参考在这里
在纯JS中,您可以向后循环数组,因此splice()
不会混淆循环中下一个元素的索引:
for (var i = arr.length - 1; i >= 0; i--) { if ( yuck(arr[i]) ) { arr.splice(i, 1); } }
我build议你使用Array.prototype.filter
var valuesArr = ["v1","v2","v3","v4","v5"]; var removeValFrom = [0, 2, 4]; valuesArr = valuesArr.filter(function(value, index) { return removeValFrom.indexOf(index) == -1; })
感觉有必要用O(n)
时间发表答案:)。 拼接解决scheme的问题是,由于数组的基本实现是字面上的数组 ,所以每个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
复制操作是二次的。
拼接解决scheme(假设已经按降序sorting以除去开销)如下所示:
for(var i=0; i<is.length; i++) xs.splice(is[i], 1)
然而,通过从头开始重新构build数组,使用掩码来查看我们是否复制元素(sorting会将它推送到O(n)log(n)
),不难实现线性时间解决scheme。 下面是这样一个实现(不是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
,splice方法的速度慢了90%。 对于较大的n
这种差异会更大。
您可以通过将removeValFromIndex
replace为removeValFromIndex.reverse()
来更正您的代码。 如果该数组不能保证使用升序,则可以使用removeValFromIndex.sort(function(a, b) { return b - a })
。
这是一个可能性:
valuesArr = removeValFromIndex.reduceRight(function (arr, it) { arr.splice(it, 1); return arr; }, valuesArr.sort(function (a, b) { return b - a }));
例如jsFiddle
在Array.prototype.reduceRight上的MDN
如果您使用的是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"]
一个简单的解决scheme使用ES5。 这似乎更适合现在的大多数应用程序,因为许多人不再想依靠jQuery等
当要删除的索引按升序sorting时:
var valuesArr = ["v1", "v2", "v3", "v4", "v5"]; var removeValFromIndex = [0, 2, 4]; // ascending removeValFromIndex.reverse().forEach(function(index) { valuesArr.splice(index, 1); });
当要删除的索引没有sorting时:
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); });
filter + indexOf (IE9 +):
function removeMany(array, indexes) { return array.filter(function(_, idx) { return indexes.indexOf(idx) === -1; }); });
或者用ES6 filter + find (Edge +):
function removeMany(array, indexes = []) { return array.filter((_, idx) => indexes.indexOf(idx) === -1) }
你可以尝试和使用delete array[index]
这不会完全删除元素,而是将值设置为undefined
。
听起来像应用可能是你在找什么。
也许这样的事情会工作?
Array.prototype.splice.apply(valuesArray, removeValFromIndexes );
这里是接受答案的一些更优化的版本。 对于一个我们可以调整拼接调用,以便我们删除一个调用中的多个连续的元素。 另一个优化是如果我们已经从数组中删除所有元素(在打字稿中),则尽早打破循环:
function removeFromArray(arr: any[], elements: any[]) { let deleteCount = 0, total = elements.length for (let i = arr.length; i--;) { if (~elements.indexOf(arr[i])) { deleteCount++ // optimize removal of consecutive elements } else if (deleteCount) { arr.splice(i + 1, deleteCount) if ((total -= deleteCount) === 0) { // if we removed all already, break early deleteCount = 0 break } deleteCount = 0 } } if (deleteCount) { arr.splice(0, deleteCount) } }