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

我有以下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);
}
}
371484 次浏览

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

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

这样就不会超过上界。

EDIT:在if语句中增加了一个减量。

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

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

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

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

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

这种方法的算法复杂度是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;
});

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

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

如果你把它们从末端取出来也没关系因为下标会在逆向过程中保留下来。

尝试在循环时将数组中继到newArray:

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;

下面是另一个正确使用拼接的例子。本例将从'array'中删除'attribute'。

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

另一个简单的方法是一次消化数组元素:

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
}

你可以浏览并使用shift()

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

Auction.auctions = Auction.auctions.filter((auction) => {
auction['seconds'] --;
return (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个元素 而迭代。< / p >

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

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

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

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

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

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

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

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


}

Array.slice方法对于实现是极其重要的 函数式编程中的不可变性à la redux

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

当我运行这个代码片段时,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)

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

举两个例子:

一个例子

// Remove from Listing the Items Checked in Checkbox for Delete
let temp_products_images = store.state.c_products.products_images
if (temp_products_images != null) {
for (var l = temp_products_images.length; l--;) {
// 'mark' is the checkbox field
if (temp_products_images[l].mark == true) {
store.state.c_products.products_images.splice(l,1);         // THIS WORKS
// this.$delete(store.state.c_products.products_images,l);  // THIS ALSO WORKS
}
}
}

两个例子

// Remove from Listing the Items Checked in Checkbox for Delete
let temp_products_images = store.state.c_products.products_images
if (temp_products_images != null) {
let l = temp_products_images.length
while (l--)
{
// 'mark' is the checkbox field
if (temp_products_images[l].mark == true) {
store.state.c_products.products_images.splice(l,1);         // THIS WORKS
// this.$delete(store.state.c_products.products_images,l);  // THIS ALSO WORKS
}
}
}

试试吧

RemoveItems.forEach((i, j) => {
OriginalItems.splice((i - j), 1);
});

删除参数

        oldJson=[{firstName:'s1',lastName:'v1'},
{firstName:'s2',lastName:'v2'},
{firstName:'s3',lastName:'v3'}]
        

newJson = oldJson.map(({...ele}) => {
delete ele.firstName;
return ele;
})

它删除和创建新的数组,因为我们在每个对象上使用展开运算符,所以原始数组对象也不会受到损害

为什么在.splice上浪费CPU周期?该操作必须一次又一次地执行整个循环以删除数组中的一个元素。

为什么不只是在一个循环中使用传统的2个旗帜?

const elements = [1, 5, 5, 3, 5, 2, 4];
const remove = 5


i = 0


for(let j = 0; j < elements.length; j++){
if (elements[j] !== remove) {
elements[i] = elements[j]
i++
}
}
elements.length = i

普通的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