Dizi boyunca döngü ve öğeleri döngüye girmeden kaldırma


463

Döngü için aşağıdaki var ve splice()bir öğeyi kaldırmak için kullandığımda , o zaman 'saniye' undefined olsun. Tanımsız olup olmadığını kontrol edebilirim, ancak bunu yapmanın muhtemelen daha zarif bir yolu olduğunu hissediyorum. Arzu, bir öğeyi silmek ve devam etmek.

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
Geriye doğru yinelemeye ve uzunluğu ayarlamaya ek olarak, sadece istediğiniz üyeleri yeni bir diziye koyabilirsiniz.
Mart'ta RobG

2
Neden Auction.auctions[i]['seconds']--yerine söylüyorsun auction.seconds--?
Don Hatch

muhtemelen önceden tanımlanmış işleve bakmak istersiniz .shift ();
raku

Yanıtlar:


856

Dizi, bir dizini yaptığınızda yeniden dizine eklenir .splice(), yani bir dizini kaldırdığınızda bir dizini atlarsınız ve önbelleğiniz .lengthgeçersizdir.

Düzeltmek için, ya a'dan isonra .splice()azaltmanız veya tersine yineleme yapmanız gerekir ...

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

Bu şekilde, yeniden dizinleme yinelemedeki bir sonraki öğeyi etkilemez, çünkü dizinleme yalnızca geçerli noktadan Dizinin sonuna kadar olan öğeleri etkiler ve yinelemedeki bir sonraki öğe geçerli noktadan daha düşüktür.


151

Bu oldukça yaygın bir konudur. Çözüm, geriye doğru döngü yapmaktır:

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

Onları sonuna kadar atmanızın bir önemi yok, çünkü geri döndüğünüzde endeksler korunacaktır.


48

Her seferinde uzunluğu başlangıçta değil döngü boyunca yeniden hesaplayın, örneğin:

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

Bu şekilde sınırları aşmayacaksınız.

EDIT: if deyimine bir azalma ekledi.


32

Sorunuz öğelerin yinelenen diziden silinmesi ve öğelerin verimli bir şekilde kaldırılmasıyla ilgili olmasa da (diğer işlemlere ek olarak), benzer bir durumda bunu yeniden düşünmesi gerektiğini düşünüyorum.

Bu yaklaşımın algoritmik karmaşıklığı O(n^2)splice fonksiyonu gibidir ve for döngüsü hem dizi üzerinde tekrar eder (splice fonksiyonu dizinin tüm elemanlarını en kötü durumda kaydırır). Bunun yerine, gerekli öğeleri yeni diziye itebilir ve daha sonra bu diziyi istenen değişkene atayabilirsiniz (bu, sadece yinelenen).

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'ten beri hepsini Array.prototype.filtertek bir satıra sığdırmak için kullanabiliriz :

Auction.auctions = Auction.auctions.filter(auction => --auction.seconds >= 0);

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

10

ES6 + kullanıyorsanız - neden sadece Array.filter yöntemini kullanmıyorsunuz?

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

Filtre yinelemesi sırasında dizi öğesinin değiştirilmesinin yalnızca nesneler için ve ilkel değerler dizisi için çalışmadığını unutmayın.


9

Dizi öğelerini bir kez sindirmek için başka bir basit çözüm:

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

Eklemenin doğru kullanımı için başka bir örnek. Bu örnek, 'öznitelik'i' dizi'den kaldırmak üzeredir.

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

8

Çalışma zamanı O (n olan bir döngü kodu olan ek yeri () ile bu çok temel soruya cevap vardır her kişiye 2 bu soru yayınlanmıştır beri yedi yıl boyunca, böyle bir cevap upvoted etti ya): yapmalısınız utanmak .

İşte bu basit doğrusal zaman problemine basit bir doğrusal zaman çözümü.

Bu snippet'i n = 1 milyonla çalıştırdığımda, filterInPlace () öğesine yapılan her çağrı .013 ila .016 saniye sürer. İkinci dereceden bir çözüm (örneğin kabul edilen cevap) milyonlarca kez alacaktır.

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

Bunun yeni bir dizi oluşturmak yerine orijinal diziyi değiştirdiğini unutmayın; bunun gibi bir yerde yapılması avantajlı olabilir, örneğin dizinin programın tek bellek darboğazı olması durumunda; bu durumda, geçici olarak bile olsa aynı boyutta başka bir dizi oluşturmak istemezsiniz.


Bir dizinin uzunluğunu atayabileceğinizi hiç fark etmemiştim!
Michael

Array.splice(i,1)Her seferinde yeni bir dizi örneği oluşturacağını bilmiyordum . Çok utanıyorum.
dehart

2
@dehart Ha, good :-) Aslında her seferinde yeni bir dizi örneği oluşturmuyor; ancak indeksi i aşağıdan daha büyük olan her öğeyi ortalama n / 2 çarpma ile çarpmak zorundadır.
Don Hatch

1

Bu konuda zaten çok güzel cevaplar var. Ancak ES5 bağlamında "nth element diziden kaldır" çözmeye çalıştığımda deneyimimi paylaşmak istedim.

JavaScript dizilerinin başlangıçtan veya bitişten öğe eklemek / kaldırmak için farklı yöntemleri vardır. Bunlar:

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 

Esasen, yukarıdaki yöntemlerin hiçbiri doğrudan nth elemanı diziden çıkarmak için kullanılamaz.

Dikkat çeken bir gerçek, bunun, yineleme sırasında bir koleksiyon için n. Öğeyi kaldırmanın mümkün olduğu java yineleyicinin kullanımının aksine olmasıdır.

Bu temelde Array.splicenth öğesinin kaldırılması için sadece bir dizi yöntemi ile bırakır (bu yöntemlerle de yapabileceğiniz başka şeyler vardır, ancak bu soru bağlamında öğelerin kaldırılmasına odaklanıyorum):

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

Orijinal yanıttan kopyalanan kod (yorumlarla birlikte):

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

Dikkate değer bir başka yöntem de Array.slice. Ancak bu yöntemin dönüş türü kaldırılan öğelerdir. Ayrıca bu orijinal diziyi değiştirmez. Değiştirilmiş kod snippet'i aşağıdaki gibi:

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

Bunu söyledikten sonra, yine Array.slicede aşağıda gösterildiği gibi nth öğeyi kaldırmak için kullanabiliriz . Ancak çok daha fazla kod (dolayısıyla verimsiz)

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.sliceYöntem la redux à fonksiyonel programlamada immutability elde etmek son derece önemlidir


Daha fazla kodun, bir kodun verimliliğinin bir ölçüsü olmaması gerektiğini unutmayın.
kano

0

Döngü yaparken bir diziyi newArray'e aktarmayı deneyin:

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

Çalışan iki örnek:

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

0

Bunu bir dene

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

-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
Bir güzel cevap bu OP için ancak bunu gelecek ziyaretçiler için değil, bu şekilde yapıldığını neden hep ne yapıldı bir açıklama elde eder ve bunun.
B001 ᛦ

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.