我有以下for循环,当我使用splice()删除一个项目时,我得到'seconds'是未定义的。我可以检查它是否未定义,但我觉得可能有一种更优雅的方式来做到这一点。他们的愿望是简单地删除一个项目,然后继续前进。

for (i = 0, len = Auction.auctions.length; i < len; i++) {
    auction = Auction.auctions[i];
    Auction.auctions[i]['seconds'] --;
    if (auction.seconds < 0) { 
        Auction.auctions.splice(i, 1);
    }           
}

当前回答

这是这个简单线性时间问题的一个简单线性时间解。

当我运行这个代码片段时,n = 100万,每次调用filterInPlace()需要0.013到0.016秒。一个二次解(例如,公认的答案)将需要它的一百万倍左右。

// Remove from array every item such that !condition(item). function filterInPlace(array, condition) { var iOut = 0; for (var i = 0; i < array.length; i++) if (condition(array[i])) array[iOut++] = array[i]; array.length = iOut; } // Try it out. A quadratic solution would take a very long time. var n = 1*1000*1000; console.log("constructing array..."); var Auction = {auctions: []}; for (var i = 0; i < n; ++i) { Auction.auctions.push({seconds:1}); Auction.auctions.push({seconds:2}); Auction.auctions.push({seconds:0}); } console.log("array length should be "+(3*n)+": ", Auction.auctions.length) filterInPlace(Auction.auctions, function(auction) {return --auction.seconds >= 0; }) console.log("array length should be "+(2*n)+": ", Auction.auctions.length) filterInPlace(Auction.auctions, function(auction) {return --auction.seconds >= 0; }) console.log("array length should be "+n+": ", Auction.auctions.length) filterInPlace(Auction.auctions, function(auction) {return --auction.seconds >= 0; }) console.log("array length should be 0: ", Auction.auctions.length)

注意,这只是修改原始数组,而不是创建一个新数组;这样做是有好处的,例如,在数组是程序的单一内存瓶颈的情况下;在这种情况下,您不希望创建另一个相同大小的数组,即使是临时的。

其他回答

虽然你的问题是关于从被迭代的数组中删除元素,而不是关于有效地删除元素(除了一些其他处理),但我认为如果遇到类似情况,应该重新考虑它。

这种方法的算法复杂度是O(n^2)作为拼接函数和for循环都遍历数组(在最坏的情况下,拼接函数移位数组的所有元素)。相反,您可以将所需的元素推入到新数组中,然后将该数组赋值给所需的变量(该变量刚刚被迭代)。

var newArray = [];
for (var i = 0, len = Auction.auctions.length; i < len; i++) {
    auction = Auction.auctions[i];
    auction.seconds--;
    if (!auction.seconds < 0) { 
        newArray.push(auction);
    }
}
Auction.auctions = newArray;

自ES2015以来,我们可以使用Array.prototype.filter将所有内容都放在一行中:

Auction.auctions = Auction.auctions.filter(auction => --auction.seconds >= 0);

在这个帖子里已经有很多精彩的答案了。然而,我想分享我的经验,当我试图解决“从数组中删除第n个元素”在ES5上下文中。

JavaScript数组有不同的方法从开始或结束添加/删除元素。这些都是:

arr.push(ele) - To add element(s) at the end of the array 
arr.unshift(ele) - To add element(s) at the beginning of the array
arr.pop() - To remove last element from the array 
arr.shift() - To remove first element from the array 

基本上上述方法都不能直接用于从数组中删除第n个元素。

一个值得注意的事实是,这与java迭代器的相反 使用它可以删除集合的第n个元素 而迭代。

这基本上只留给我们一个数组方法array。splice来执行删除第n个元素(你也可以用这些方法做其他事情,但在这个问题的上下文中,我主要关注的是删除元素):

Array.splice(index,1) - removes the element at the index 

以下是从原始答案复制的代码(带有注释):

Var arr = [" 1 ", " 2 ", " 3 ", " 4 "]; Var I = arrr .length;//初始化计数器为数组长度 while (i——)//递减计数器,否则将遇到IndexOutBounds异常 { If (arr[i] === "four" || arr[i] === "two") { //splice修改原始数组 加勒比海盗。拼接(我,1);//永远不会遇到IndexOutBounds异常 console.log(“元素移除。加勒比海盗:”); }其他{ console.log("元素未删除。加勒比海盗:”); } console.log (arr); }

另一个值得注意的方法是Array.slice。然而,此方法的返回类型是删除的元素。这也不会修改原始数组。修改后的代码片段如下:

Var arr = [" 1 ", " 2 ", " 3 ", " 4 "]; Var I = arrr .length;//初始化计数器为数组长度 While (i——)//自减计数器 { If (arr[i] === "four" || arr[i] === "two") { console.log(“元素移除。加勒比海盗:”); console.log(加勒比海盗。Slice (i, i + 1)); console.log("原始数组:"); console.log (arr); } }

话虽如此,我们仍然可以使用Array。切片以删除第n个元素,如下所示。然而,它有更多的代码(因此效率低)

Var arr = [" 1 ", " 2 ", " 3 ", " 4 "]; Var I = arrr .length;//初始化计数器为数组长度 While (i——)//自减计数器 { If (arr[i] === "four" || arr[i] === "two") { console.log("删除第i个元素后的数组:"); Arr = Arr。片(0,1).concat(加勒比海盗。Slice (i + 1)); console.log (arr); } }

数组。切片法的实现极为重要 函数式编程中的不可变性à la redux

普通的for循环对我来说更熟悉,我只需要在每次从数组中删除一个项时递减索引

//5个正确,5个错误 Var arr1 =[假,假,真,真,假,真,假,真,真,假]; //从数组中删除false For (var I = 0;I < arr1.length;我+ +){ If (arr1[i] === false){ arr1。拼接(我,1); I——;//如果item被移除,则递减索引 } } Console.log (arr1);//应该是5个true

当您执行.splice()时,数组正在重新索引,这意味着当一个索引被删除时,您将跳过一个索引,并且缓存的.length已过时。

要修复它,你要么需要在.splice()后面递减i,要么简单地反向迭代…

var i = Auction.auctions.length
while (i--) {
    ...
    if (...) { 
        Auction.auctions.splice(i, 1);
    } 
}

这样,重新索引就不会影响迭代中的下一项,因为索引只影响从当前点到数组末尾的项,并且迭代中的下一项低于当前点。

如果你正在使用ES6+ -为什么不直接使用数组。过滤方法?

Auction.auctions = Auction.auctions.filter((auction) => {
  auction['seconds'] --;
  return (auction.seconds > 0)
})  

注意,在过滤器迭代过程中修改数组元素只对对象有效,而对基元值数组无效。