Swift'de numaralandırma sırasında diziden çıkarılsın mı?


86

Swift'de bir dizi aracılığıyla numaralandırmak ve belirli öğeleri kaldırmak istiyorum. Bunun güvenli olup olmadığını ve değilse bunu nasıl başarmam gerektiğini merak ediyorum.

Şu anda bunu yapıyorum:

for (index, aString: String) in enumerate(array) {
    //Some of the strings...
    array.removeAtIndex(index)
}

Yanıtlar:


72

Swift 2'de bunu kullanmak oldukça kolaydır enumerateve reverse.

var a = [1,2,3,4,5,6]
for (i,num) in a.enumerate().reverse() {
    a.removeAtIndex(i)
}
print(a)

1
Çalışıyor, ancak filtre gerçekten gitmenin yolu

13
@Mayerz Yanlış. " Swift'de bir dizi aracılığıyla numaralandırmak ve belirli öğeleri kaldırmak istiyorum." filteryeni bir dizi döndürür. Diziden hiçbir şey kaldırmıyorsunuz. filterBir numaralandırma bile çağırmazdım . Bir kedinin derisini yüzmenin her zaman birden fazla yolu vardır.
Johnston

6
doğru, benim hatam! Pla dont skin any cats

56

Bir filteryolunu düşünebilirsiniz :

var theStrings = ["foo", "bar", "zxy"]

// Filter only strings that begins with "b"
theStrings = theStrings.filter { $0.hasPrefix("b") }

Parametresi, filterdizi tipi bir örnek alan (bu durumda String) ve bir Bool. Sonuç olduğu truezaman elemanı tutar, aksi takdirde eleman filtrelenir.


16
filterDiziyi güncellemediğini açıkça belirtmek isterim , sadece yeni bir tane döndürür
Antonio

Parantezler silinmelidir; bu takip eden bir kapanış.
Jessy

@Antonio haklısın. Gerçekten de bu yüzden onu daha güvenli bir çözüm olarak yayınladım. Büyük diziler için farklı bir çözüm düşünülebilir.
Matteo Piombo

Hm, dediğiniz gibi bu yeni bir dizi döndürüyor. O zaman filteryöntemi bir mutatingtane yapmak mümkün mü ( mutatinganahtar kelimeyi okuduğum gibi selfbunun yerine bunun gibi işlevlerin değiştirilmesini sağlar )?
Gee.E

@ Gee.E kesinlikle bir yerinde filtreyi, Arrayonu mutatingsoru koduna benzer şekilde işaretlemek için bir uzantı olarak ekleyebilirsiniz . Her neyse, bunun her zaman bir avantaj olmayabileceğini düşünün. Her neyse, bir nesneyi her kaldırdığınızda, diziniz bellekte yeniden düzenlenebilir . Bu nedenle, yeni bir dizi tahsis etmek daha verimli olabilir ve ardından filtre fonksiyonunun sonucuyla atomik bir ikame yapabilir. Derleyici, kodunuza bağlı olarak daha da fazla optimizasyon yapabilir.
Matteo Piombo

38

In Swift 3. ve 4. , bu şöyle olacaktır:

Johnston'ın cevabına göre sayılarla:

var a = [1,2,3,4,5,6]
for (i,num) in a.enumerated().reversed() {
   a.remove(at: i)
}
print(a)

İle dizeleri OP'ın soru olarak:

var b = ["a", "b", "c", "d", "e", "f"]

for (i,str) in b.enumerated().reversed()
{
    if str == "c"
    {
        b.remove(at: i)
    }
}
print(b)

Ancak şimdi Swift 4.2 veya sonraki sürümlerde, Apple tarafından WWDC2018'de önerilen daha iyi ve daha hızlı bir yol var:

var c = ["a", "b", "c", "d", "e", "f"]
c.removeAll(where: {$0 == "c"})
print(c)

Bu yeni yöntemin birçok avantajı vardır:

  1. İle uygulamalardan daha hızlıdır filter.
  2. Dizileri ters çevirme ihtiyacını ortadan kaldırır.
  3. Öğeleri yerinde kaldırır ve böylece yeni bir dizi tahsis etmek ve geri döndürmek yerine orijinal diziyi günceller.

ya öğe bir nesneyse ve onu kontrol etmem gerekiyorsa, {$0 === Class.self}çalışmıyorsa
TomSawyer

14

Belirli bir dizindeki bir öğe bir diziden çıkarıldığında, sonraki tüm öğelerin konumları (ve dizinleri) değişir, çünkü bir konum geri kayarlar.

Bu yüzden en iyi yol, dizide ters sırada gezinmektir - ve bu durumda geleneksel bir for döngüsü kullanmanızı öneririm:

for var index = array.count - 1; index >= 0; --index {
    if condition {
        array.removeAtIndex(index)
    }
}

Ancak bence en iyi yaklaşım, filter@perlfly'nin cevabında anlattığı gibi yöntemi kullanmaktır .


ancak ne yazık ki hızlı 3'te kaldırıldı
Sergey Brazhnik

4

Hayır, numaralandırma sırasında dizileri değiştirmek güvenli değildir, kodunuz çökecektir.

Yalnızca birkaç nesneyi silmek istiyorsanız, filterişlevi kullanabilirsiniz .


3
Bu Swift için yanlıştır. Diziler değer türleridir, bu nedenle işlevlere aktarıldıklarında, değişkenlere atandıklarında veya numaralandırmada kullanıldıklarında "kopyalanırlar". (Swift, değer türleri için yazma üzerine kopyalama işlevi uygular, bu nedenle gerçek kopyalama minimumda tutulur.) Doğrulamak için aşağıdakileri deneyin: var x = [1, 2, 3, 4, 5]; baskı (x); var i = 0; x in v için {if (v% 2 == 0) {x.remove (at: i)} else {i + = 1}}; baskı (x)
404compilerfoundfound

Evet, ne yaptığınızı tam olarak bilmeniz koşuluyla, haklısınız. Belki cevabımı net bir şekilde ifade etmedim. Dediğim gerekirdi Mümkün ama bu güvenli değil . Güvenli değil çünkü kapsayıcı boyutunu değiştiriyorsunuz ve kodunuzda bir hata yaparsanız, uygulamanız çökecektir. Swift, çalışma zamanında beklenmedik bir şekilde çökmeyecek güvenli kod yazmakla ilgilidir. Gibi functionnal programlama fonksiyonları kullanarak yüzden filterolduğunu daha güvenli . İşte benim aptal bir örneğim:var y = [1, 2, 3, 4, 5]; print(y); for (index, value) in y.enumerated() { y.remove(at: index) } print(y)
Starscream

Hızlı numaralandırma veya hatta C # 'ın koleksiyon türleri ile NSArray aracılığıyla yineleme yaparken bunu yapmanın istisna atma davranışının aksine Swift'de numaralandırılan koleksiyonu değiştirmenin mümkün olduğunu ayırt etmek istedim. Burada bir istisna atacak olan değişiklik değil, dizinleri yanlış yönetme ve sınırların dışına çıkma olasılığı (çünkü boyutu azaltmışlardı). Ancak, koleksiyonları manipüle etmek için işlevsel programlama yöntemlerini kullanmanın genellikle daha güvenli ve daha net olduğu konusunda size kesinlikle katılıyorum. Özellikle Swift'de.
404compilernotfound

2

Ya silinecek öğeleri depolamak için değiştirilebilir bir dizi oluşturun ve ardından numaralandırmadan sonra bu öğeleri orijinalden kaldırın. Veya dizinin bir kopyasını oluşturun (değişmez), bunu numaralandırın ve numaralandırma sırasında orijinalden nesneleri (dizine göre değil) kaldırın.


2

Geleneksel for döngüsü basit bir while döngüsü ile değiştirilebilir; bu, çıkarmadan önce her öğe üzerinde başka işlemler de gerçekleştirmeniz gerekiyorsa yararlıdır.

var index = array.count-1
while index >= 0 {

     let element = array[index]
     //any operations on element
     array.remove(at: index)

     index -= 1
}

1

Numaralandırma sırasında öğeleri nil olarak ayarlamanızı ve tamamlandıktan sonra tüm boş öğeleri arrays filter () yöntemini kullanarak kaldırmanızı öneririm.


1
Bu, yalnızca depolanan tür isteğe bağlıysa çalışır. Ayrıca filteryöntemin kaldırmadığını, yeni bir dizi oluşturduğunu unutmayın.
Antonio

Katılıyorum. Ters düzen daha iyi bir çözümdür.
freele

0

Eklemek gerekirse, birden fazla diziniz varsa ve A dizisinin N dizinindeki her öğe, B dizisinin N diziniyle ilişkiliyse, o zaman numaralandırılmış diziyi tersine çevirmek için yöntemi kullanmaya devam edebilirsiniz (geçmişteki yanıtlar gibi). Ancak diğer dizilerin öğelerine erişirken ve bunları silerken bunları tersine çevirmeye gerek olmadığını unutmayın.

Like so, (one can copy and paste this on Playground)

var a = ["a", "b", "c", "d"]
var b = [1, 2, 3, 4]
var c = ["!", "@", "#", "$"]

// remove c, 3, #

for (index, ch) in a.enumerated().reversed() {
    print("CH: \(ch). INDEX: \(index) | b: \(b[index]) | c: \(c[index])")
    if ch == "c" {
        a.remove(at: index)
        b.remove(at: index)
        c.remove(at: index)
    }
}

print("-----")
print(a) // ["a", "b", "d"]
print(b) // [1, 2, 4]
print(c) // ["!", "@", "$"]
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.