循环遍历数组并删除项目,而不会中断 for 循环

IT技术 javascript loops
2021-02-03 23:39:28

我有以下 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);
    }           
}
6个回答

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

要修复它,您要么需要i在 a 之后递减.splice(),要么只是反向迭代......

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

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

想知道是否length === 0会在无限循环中结束,我已经尝试过这个解决方案并且(当然它确实有效),因为它首先评估 的值i然后递减。然而,--(和++)的行为如此怪异,以至于像 swift 这样的现代语言不再支持它们。我认为它们是不好的做法(至少在这种情况下)。
2021-03-16 23:39:28

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

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

如果您将它们从最后弹出并不重要,因为索引将在您倒退时保留。

这个向后循环的想法拯救了我的一天。谢谢
2021-03-12 23:39:28

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

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
      }
}

这样你就不会越界。

编辑:在 if 语句中添加了一个递减。

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

这种方法的算法复杂性是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);
Auction.auctions = Auction.auctions.filter(function(el) {
  return --el["seconds"] > 0;
});