for 루프를 끊지 않고 배열을 반복하고 항목을 제거


462

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

11
거꾸로 반복하고 길이를 조정하는 것 외에도 원하는 멤버를 새 배열에 넣을 수도 있습니다.
RobG

2
Auction.auctions[i]['seconds']--대신에 말 auction.seconds--합니까?
Don Hatch

미리 정의 된 함수를보고 싶을 것입니다. .shift ();
raku

답변:


856

을 수행하면 배열의 색인이 다시 생성되므로 .splice()색인을 제거하면 색인을 건너 뛰고 캐시 .length가 더 이상 사용되지 않습니다.

그것을 고치려면, i후에 감소 .splice()하거나 단순히 반대로 반복 해야합니다 ...

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

이렇게하면 재색 인화는 반복의 다음 항목에 영향을 미치지 않습니다. 색인화는 현재 점에서 배열 끝까지의 항목에만 영향을 미치고 반복의 다음 항목은 현재 점보다 낮기 때문입니다.


151

이것은 매우 일반적인 문제입니다. 해결책은 뒤로 루프하는 것입니다.

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

뒤로 갈수록 인덱스가 유지되므로 끝에서 튀어 나와도 상관 없습니다.


48

다음과 같이 처음부터 반복하는 대신 루프를 통해 매번 길이를 다시 계산하십시오.

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 문에 감소가 추가되었습니다.


32

귀하의 질문은 반복되는 배열 에서 요소를 삭제 하는 것이지 다른 요소를 효율적으로 제거하는 것이 아니라 다른 상황에서 비슷한 요소가 있으면 다시 고려해야한다고 생각합니다.

이 접근법의 알고리즘 복잡도는 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);

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

10

ES6 +를 사용하는 경우 왜 Array.filter 메소드를 사용하지 않습니까?

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

필터 반복 동안 배열 요소를 수정하면 객체에 대해서만 작동하며 기본 값의 배열에는 작동하지 않습니다.


9

배열 요소를 한 번 요약하는 또 다른 간단한 솔루션 :

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
}

8

다음은 스플 라이스를 올바르게 사용하기위한 또 다른 예입니다. 이 예는 '배열'에서 '속성'을 제거하려고합니다.

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

8

실행 시간 O (n은이 루프에서 코드를 가진 스플 라이스 ()이 매우 기본적인 질문에 응답 한 모든 사람 (2) 이 질문이 게시 된 이후 일곱 년 동안, 같은 대답을 upvoted있다, 또는) : 당신이해야 부끄러워 .

다음은이 간단한 선형 시간 문제에 대한 간단한 선형 시간 솔루션입니다.

n = 1 백만 으로이 코드를 실행하면 filterInPlace ()에 대한 각 호출에 .013 ~ .016 초가 걸립니다. 2 차 해법 (예 : 수용된 답)은 백만 배나 걸릴 것입니다.

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

이렇게하면 새 배열을 만들지 않고 원래 배열이 수정됩니다. 예를 들어, 어레이가 프로그램의 단일 메모리 병목 인 경우에 이와 같이 제자리에이를 수행하는 것이 유리할 수있다; 이 경우 일시적으로도 동일한 크기의 다른 배열을 만들고 싶지 않습니다.


배열의 길이를 지정할 수 있다는 것을 결코 깨닫지 못했습니다!
Michael

Array.splice(i,1)매번 새로운 배열 인스턴스를 만들지 몰랐 습니다. 매우 부끄럽습니다.
dehart

2
@dehart Ha, good :-) 실제로 매번 새로운 배열 인스턴스를 만들지는 않습니다. 그러나 인덱스가 i보다 큰 모든 항목을 범프해야합니다. 평균 n / 2 범프입니다.
Don Hatch

1

이 스레드에는 이미 훌륭한 답변이 많이 있습니다. 그러나 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 번째 요소를 제거하는 데 직접 사용할 수 없습니다.

주목할만한 사실은 이것이 반복하는 동안 컬렉션의 n 번째 요소를 제거 할 수있는 Java 반복자의 사용과 대조적이라는 것입니다.

이것은 기본적으로 Array.splicen 번째 요소 제거를 수행하는 배열 방법 을 하나만 남겨 둡니다 (이 방법 으로 수행 할 수있는 다른 작업도 있지만이 질문의 맥락에서 요소 제거에 중점을 둡니다).

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방법은 기능 프로그래밍에서 불변성을 달성하는 데 매우 중요합니다.


더 많은 코드가 코드의 효율성을 측정하는 것은 아닙니다.
kano

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;

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


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

7
좋은 대답은 그것이 OP뿐만 SO 미래의 방문자뿐만 아니라, 같은 방식으로 이루어졌다 왜 항상 일이 있었는지에 대한 설명을하고 있습니다.
B001 ᛦ

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.