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

时间:2012-03-27 01:44:08

标签: 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);
    }           
}

18 个答案:

答案 0 :(得分:764)

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

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

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

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

答案 1 :(得分:119)

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

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

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

答案 2 :(得分:40)

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

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语句中添加了一个减量。

答案 3 :(得分:27)

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

这种方法的算法复杂度为O(n^2),因为splice函数和for循环都在数组上迭代(splice函数在最坏的情况下移位数组的所有元素)。相反,您只需将所需元素推送到新数组,然后将该数组分配给所需的变量(只是迭代)。

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

答案 4 :(得分:16)

Auction.auction = Auction.auctions.filter(function(el) {
  return --el["seconds"] > 0;
});

答案 5 :(得分:9)

这是正确使用拼接的另一个例子。这个例子即将删除&#39;属性&#39;来自&#39;阵列&#39;。

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

答案 6 :(得分:8)

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

    <RelativeLayout  android:layout_width="fill_parent" android:layout_height="wrap_content">
        <TextView android:textAppearance="?android:textAppearanceLarge" android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="58.0dip" android:text="@string/naziv" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" />
    </RelativeLayout>

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/scrollView" >

        <LinearLayout android:layout_gravity="center" android:orientation="vertical" android:paddingLeft="30.0dip" android:paddingTop="90.0dip" android:paddingRight="30.0dip"  android:paddingBottom="20dp" android:layout_width="fill_parent" android:layout_height="wrap_content">
            <Button android:id="@+id/button0"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_veter"  />
            <Button android:id="@+id/button4"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_plimovanje" />
            <Button android:id="@+id/button1"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_trenutni_podatki" />
            <Button android:id="@+id/button5"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_video" />
            <Button android:id="@+id/button2"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_mbp" />
            <Button android:id="@+id/button10"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_wsg"/>
            <Button android:id="@+id/button11"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_ath" />
            <Button android:id="@+id/button12"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_sts" />
            <Button android:id="@+id/button13"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_w" />
            <Button android:id="@+id/button14"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_dotb" />
            <Button android:id="@+id/button15"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_ast" />
            <Button android:id="@+id/button16"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_caw" />
            <Button android:id="@+id/button17"  android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/gumb_pado" />


        </LinearLayout>
    </ScrollView>



</LinearLayout>

答案 7 :(得分:5)

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

Auction.auctions = Auction.auctions.filter((auction) => {
  auction['seconds'] --;
  return (auction.seconds > 0)
})  

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

答案 8 :(得分:2)

此线程上已经有很多很棒的答案。但是,当我尝试解决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个元素   同时进行迭代。

这基本上只给我们一个数组方法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方法对于实现   函数式编程中的不变性

答案 9 :(得分:1)

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

答案 10 :(得分:1)

为什么要在 .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

答案 11 :(得分:0)

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

//5 trues , 4 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

答案 12 :(得分:0)

删除参数

        oldJson=[{firstName:'s1',lastName:'v1'},
                 {firstName:'s2',lastName:'v2'},
                 {firstName:'s3',lastName:'v3'}]
        
        newJson = oldJson.map(({...ele}) => {
          delete ele.firstName;
          return ele;
          })

它删除和创建新数组,因为我们在每个对象上使用扩展运算符,所以原始数组对象也没有受到伤害

答案 13 :(得分:0)

尝试一下

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

答案 14 :(得分:0)

两个有效的示例:

(Example ONE)
// 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
        }
    }
}

(Example TWO)
// 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
        }
    }
}

答案 15 :(得分:0)

对于每个使用循环中具有splice()的代码,运行时间为O(n 2 )的代码回答了这个非常基本的问题的人,或者对这样的回答表示赞同的人此问题发布已经七年了:you should be ashamed

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

当我运行此代码段(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)

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

答案 16 :(得分:0)

您可以查看并使用shift()

答案 17 :(得分:0)

尝试在循环时将数组中继到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;