提问人:dzm 提问时间:3/27/2012 最后编辑:JJJdzm 更新时间:8/4/2023 访问量:408777
循环遍历数组并删除项目,而不中断 for 循环
Looping through array and removing items, without breaking for loop
问:
我有以下 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);
}
}
答:
每次通过循环重新计算长度,而不仅仅是在开始时,例如:
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 语句中添加了递减。
当您执行 时,数组正在重新编制索引,这意味着当删除索引时,您将跳过索引,并且缓存已过时。.splice()
.length
要修复它,您要么需要在 之后递减,要么只是反向迭代......i
.splice()
var i = Auction.auctions.length
while (i--) {
...
if (...) {
Auction.auctions.splice(i, 1);
}
}
这样,重新索引不会影响迭代中的下一项,因为索引仅影响从当前点到 Array 末尾的项,并且迭代中的下一项低于当前点。
评论
length === 0
i
--
++
i++
++i
虽然您的问题是关于从被迭代的数组中删除元素,而不是关于有效地删除元素(除了其他一些处理),但我认为如果处于类似情况,应该重新考虑它。
这种方法的算法复杂性在于拼接函数和 for 循环都在数组上迭代(在最坏的情况下,拼接函数会移动数组的所有元素)。相反,您可以将所需的元素推送到新数组,然后将该数组分配给所需的变量(刚刚迭代)。O(n^2)
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)
})
请注意,在筛选器迭代期间修改数组元素仅适用于对象,不适用于基元值数组。
这个线程上已经有很多精彩的答案。但是,当我尝试在 ES5 上下文中解决“从数组中删除第 n 个元素”时,我想分享我的经验。
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 个元素 在迭代时。
这基本上只剩下一个数组方法来执行第 n 个元素的删除(您也可以使用这些方法执行其他操作,但在这个问题的上下文中,我专注于删除元素):Array.splice
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);
}
}
话虽如此,我们仍然可以使用删除第 n 个元素,如下所示。但是,它需要更多的代码(因此效率低下)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("Array after removal of ith element: ");
arr = arr.slice(0, i).concat(arr.slice(i + 1));
console.log(arr);
}
}
该方法对于实现极为重要 函数式编程中的不可变性 à la redux
Array.slice
评论
这是这个简单线性时间问题的简单线性时间解决方案。
当我运行此代码片段时,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
评论
const arr = [...]
const indexes = [1,2,8,9,34,67]
const result = arr.filter((_, index) => !indexes.includes(index));
console.log(result);
上一个:循环访问对象属性
下一个:循环中的 Lambda [复制]
评论
Auction.auctions[i]['seconds']--
auction.seconds--