Looping through array and removing items, without breaking for loop
我有以下for循环,当我使用
1 2 3 4 5 6 7 | 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); } } |
当您执行
要修复此问题,您需要在
1 2 3 4 5 6 7 | var i = Auction.auctions.length while (i--) { ... if (...) { Auction.auctions.splice(i, 1); } } |
这样,重新索引不会影响迭代中的下一个项目,因为索引仅影响从当前点到数组末尾的项目,并且迭代中的下一个项目低于当前点。
这是一个很常见的问题。解决方案是向后循环:
1 2 3 4 5 6 | 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); } } |
是否从头开始弹出都没关系,因为索引将在您向后移动时保留。
每次循环时都要重新计算长度,而不是一开始就重新计算长度,例如:
1 2 3 4 5 6 7 8 | 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语句中增加了一个减量。
尽管您的问题是关于从要迭代的数组中删除元素,而不是有效地删除元素(除了某些其他处理),但我认为如果处于类似情况,则应该重新考虑它。
这种方法的算法复杂度为
1 2 3 4 5 6 7 8 9 | 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开始,我们可以使用
1 | Auction.auctions = Auction.auctions.filter(auction => --auction.seconds >= 0); |
1 2 3 | Auction.auctions = Auction.auctions.filter(function(el) { return --el["seconds"] > 0; }); |
如果您正在使用ES6 +-为什么不只使用Array.filter方法?
1 2 3 4 | Auction.auctions = Auction.auctions.filter((auction) => { auction['seconds'] --; return (auction.seconds > 0) }) |
请注意,在过滤器迭代期间修改数组元素仅适用于对象,而不适用于原始值数组。
一次消化数组元素的另一种简单解决方案:
1 2 3 4 5 6 7 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 } |
这是正确使用接头的另一个示例。本示例将要删除"数组"中的"属性"。
1 2 3 4 5 | for (var i = array.length; i--;) { if (array[i] === 'attribute') { array.splice(i, 1); } } |
对于在发布此问题的七年内,使用循环运行splice()且运行时间为O(n2)的代码回答了这个非常基本的问题的每个人,或者对这样的回答提出了最高评价的人: 羞愧。
这是此简单线性时间问题的简单线性时间解决方案。
当我运行此代码段(n = 1百万)时,每次调用filterInPlace()都需要0.013到.016秒。 二次解(例如可接受的答案)将花费一百万倍左右。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // 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) |
注意,这将修改原始数组,而不是创建一个新数组。 像这样在适当的位置进行操作可能会很有利 如果数组是程序的单个内存瓶颈; 在这种情况下,您甚至不想临时创建另一个相同大小的数组。
在此线程上已经有很多精彩的答案。但是,当我尝试解决ES5上下文中的"从数组中删除第n个元素"时,我想分享自己的经验。
JavaScript数组具有不同的方法来从开始或结束添加/删除元素。这些是:
1 2 3 4 | 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个元素。
A fact worth noting is that this is in contrast with java iterator's
using which it is possible to remove nth element for a collection
while iterating.
基本上,这只给我们留下了一个数组方法
1 | Array.splice(index,1) - removes the element at the index |
这是从原始答案中复制的代码(带有注释):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var arr = ["one","two","three","four"]; var i = arr.length; //initialize counter to array length while (i--) //decrement counter else it would run into IndexOutBounds exception { if (arr[i] ==="four" || arr[i] ==="two") { //splice modifies the original array arr.splice(i, 1); //never runs into IndexOutBounds exception console.log("Element removed. arr:"); } else { console.log("Element not removed. arr:"); } console.log(arr); } |
另一个值得注意的方法是
1 2 3 4 5 6 7 8 9 10 11 12 | var arr = ["one","two","three","four"]; var i = arr.length; //initialize counter to array length while (i--) //decrement counter { if (arr[i] ==="four" || arr[i] ==="two") { console.log("Element removed. arr:"); console.log(arr.slice(i, i + 1)); console.log("Original array:"); console.log(arr); } } |
话虽如此,我们仍然可以使用
1 2 3 4 5 6 7 8 9 10 11 12 | var arr = ["one","two","three","four"]; var i = arr.length; //initialize counter to array length while (i--) //decrement counter { if (arr[i] ==="four" || arr[i] ==="two") { console.log("Array after removal of ith element:"); arr = arr.slice(0, i).concat(arr.slice(i + 1)); console.log(arr); } } |
The
Array.slice method is extremely important to achieve
immutability in functional programming à la redux
循环时尝试将数组中继到newArray中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | var auctions = Auction.auctions; var auctionIndex; var auction; var newAuctions = []; for ( auctionIndex = 0; auctionIndex < Auction.auctions.length; auctionIndex++) { auction = auctions[auctionIndex]; if (auction.seconds >= 0) { newAuctions.push( auction); } } Auction.auctions = newAuctions; |
您可以浏览并使用
1 2 3 4 5 6 7 8 9 | 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); i--; len--; } } |