循环遍历数组并删除项目,而不会破坏循环

dzm*_*dzm 430 javascript loops

我有以下for循环,当我splice()用来删除一个项目时,我得到'秒'未定义.我可以检查它是否未定义,但我觉得这可能是一种更优雅的方式.希望简单地删除一个项目并继续前进.

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);
    }           
}
Run Code Online (Sandbox Code Playgroud)

小智 807

当你执行a时.splice(),数组会被重新编入索引,这意味着当一个索引被删除时你将跳过索引,并且你的缓存.length已经过时了.

要修复它,你需要i在a之后递减.splice(),或者简单地反向迭代......

var i = Auction.auctions.length
while (i--) {
    ...
    if (...) { 
        Auction.auctions.splice(i, 1);
    } 
}
Run Code Online (Sandbox Code Playgroud)

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

  • @lukas_o 如果您简单地理解它的含义,那么就没有奇怪或意外的功能。`i++` 表示评估该值,然后递增它。`++i` 表示增加值,然后评估它。JS 永远不会做除此之外的任何事情。这确实很容易理解,并且保证每次都以完全相同的方式工作,即使您使用不同的 JS 引擎也是如此。 (9认同)

fra*_*aro 134

这是一个非常常见的问题.解决方案是向后循环:

for (var i = Auction.auctions.length - 1; i >= 0; i--) {
    Auction.auctions[i].seconds--;
    if (Auction.auctions[i].seconds < 0) { 
        Auction.auctions.splice(i, 1);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你从最后弹出它们并不重要,因为当你倒退时,索引将被保留.


Mar*_*arc 42

每次通过循环而不是在开始时重新计算长度,例如:

for (i = 0; i < Auction.auctions.length; i++) {
      auction = Auction.auctions[i];
      Auction.auctions[i]['seconds'] --;
      if (auction.seconds < 0) { 
          Auction.auctions.splice(i, 1);
          i--; //decrement
      }
}
Run Code Online (Sandbox Code Playgroud)

这样你就不会超过界限.

编辑:在if语句中添加了一个减量.


0xc*_*0de 30

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

这种方法的算法复杂性是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;
Run Code Online (Sandbox Code Playgroud)

从ES2015开始,我们可以Array.prototype.filter将它们整合在一条线上:

Auction.auctions = Auction.auctions.filter(auction => --auction.seconds >= 0);
Run Code Online (Sandbox Code Playgroud)


Aes*_*ete 17

Auction.auctions = Auction.auctions.filter(function(el) {
  return --el["seconds"] > 0;
});
Run Code Online (Sandbox Code Playgroud)


Acc*_*t م 15

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

//5 trues , 5 falses
var arr1 = [false, false, true, true, false, true, false, true, true, false];

//remove falses from array
for (var i = 0; i < arr1.length; i++){
    if (arr1[i] === false){
        arr1.splice(i, 1);
        i--;// decrement index if item is removed
    }
}
console.log(arr1);// should be 5 trues
Run Code Online (Sandbox Code Playgroud)

  • 我发现这种方法(拼接和递减)*考虑熟悉度*是最可移植/可理解的,当想要将小边缘情况从 for 循环中删除而不将循环重写为过滤器/减少/平面映射等或向后循环时(可能没有注意到) 6个月后)。其他解决方案更好/更智能,但有时只需要修补小循环边缘情况。 (2认同)

Rub*_*nsh 9

如果您正在使用ES6 +-为什么不只使用Array.filter方法?

Auction.auctions = Auction.auctions.filter((auction) => {
  auction['seconds'] --;
  return (auction.seconds > 0)
})  
Run Code Online (Sandbox Code Playgroud)

请注意,在过滤器迭代期间修改数组元素仅适用于对象,而不适用于原始值数组。


小智 8

这是正确使用拼接的另一个例子.这个例子即将从'array'中删除'attribute'.

for (var i = array.length; i--;) {
    if (array[i] === 'attribute') {
        array.splice(i, 1);
    }
}
Run Code Online (Sandbox Code Playgroud)


Pab*_*blo 8

另一个消化数组元素的简单解决方案:

while(Auction.auctions.length){
    // From first to last...
    var auction = Auction.auctions.shift();
    // From last to first...
    var auction = Auction.auctions.pop();

    // Do stuff with auction
}
Run Code Online (Sandbox Code Playgroud)


Don*_*tch 6

为了谁回答了具有码拼接()在一个循环中,其运行时间为O(n这个很基本的问题,每个人2,因为这个问题被张贴,或谁已经upvoted这样的答案,在七年):你应该感到as愧

这是此简单线性时间问题的简单线性时间解决方案。

当我运行此代码段(n = 1百万)时,每次调用filterInPlace()都需要0.013到.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)
Run Code Online (Sandbox Code Playgroud)

注意,这将修改原始数组,而不是创建一个新数组。像这样在适当的位置执行操作可能是有利的,例如,在数组是程序的单个内存瓶颈的情况下;在这种情况下,您甚至不想临时创建另一个相同大小的数组。