Nesneyi değere göre kaldırmak için dizi uzantısı


140
extension Array {
    func removeObject<T where T : Equatable>(object: T) {
        var index = find(self, object)
        self.removeAtIndex(index)
    }
}

Ancak, bir hata alıyorum var index = find(self, object)

'T', 'T' ye dönüştürülemez

Ayrıca bu yöntem imzası ile denedim: func removeObject(object: AnyObject)Ancak, aynı hatayı alıyorum:

'AnyObject', 'T'ye dönüştürülemez

Bunu yapmanın uygun yolu nedir?


T whereYöntem bildiriminizden kaldırmayı deneyin . Sadece func removeObject<T: Equatable>. Bu soru aşağıdakilerle ilgilidir: stackoverflow.com/questions/24091046/…
ahruss

Yanıtlar:


165

İtibariyle Swift 2 , bu elde edilebilir protokol uzantısı yöntemi . koleksiyonun öğeleri şu şekildeyse ( özellikle açık ) removeObject()tüm tiplerde bir yöntem olarak tanımlanır :RangeReplaceableCollectionTypeArrayEquatable

extension RangeReplaceableCollectionType where Generator.Element : Equatable {

    // Remove first collection element that is equal to the given `object`:
    mutating func removeObject(object : Generator.Element) {
        if let index = self.indexOf(object) {
            self.removeAtIndex(index)
        }
    }
}

Misal:

var ar = [1, 2, 3, 2]
ar.removeObject(2)
print(ar) // [1, 3, 2]

Swift 2 / Xcode 7 beta 2 için güncelleme : Airspeed Velocity'nin yorumlarda fark ettiği gibi, artık şablonda daha kısıtlayıcı olan genel bir türe bir yöntem yazmak mümkün, bu nedenle yöntem artık bir uzantı olarak tanımlanabilir arasında Array:

extension Array where Element : Equatable {

    // ... same method as above ...
}

Protokol uzantısı hala daha büyük tipler için geçerli olma avantajına sahiptir.

Swift 3 için güncelleme :

extension Array where Element: Equatable {

    // Remove first collection element that is equal to the given `object`:
    mutating func remove(object: Element) {
        if let index = index(of: object) {
            remove(at: index)
        }
    }
}

1
Mükemmel, Swift'i sevmelisin (2). Zamanla nasıl daha fazla şey mümkün olsun ve şeyler basitleştiğini gerçekten seviyorum
Kametrixom

1
İyi bir nokta, birçok yönden cevabın hala teknik olarak doğru, sadece deyimsel değil, daha da kötüsü - insanlar gelecek, cevabı okuyacak, ücretsiz bir fonksiyonun çözmenin doğru yolu olduğunu düşünüyorum, çünkü yüksek puan alan bir cevap . Oldukça çirkin senaryo. Meta olarak yayınlanacak.
Airspeed Velocity

1
@AirspeedVelocity: Vay canına, bunu kaçırdım. Sürüm notlarında yer alıyor mu?
Martin R

1
Eğer objc aynı özelliğe istiyorsanız, "eğer" "iken" için değiştirebilirsiniz (yani tüm eşleşen yerine sadece 1 birinin nesneleri kaldırır)
powertoold

2
Swift 3 sürümü harika, ancak Swift API tasarım yönergelerineremove(object: Element) uymak ve ayrıntılardan kaçınmak için beyanını biraz yeniden adlandırırım . Bunu yansıtan bir düzenleme gönderdim.
swiftcode

66

Şablonda daha kısıtlayıcı olan genel bir türe bir yöntem yazamazsınız.

NOT : Swift 2.0 itibariyle, artık yöntemlerini yazabilir olan şablona daha kısıtlayıcı. Kodunuzu 2.0 sürümüne yükselttiyseniz, uzantıları kullanarak bunu uygulamak için yeni seçenekler için diğer yanıtlara bakın.

Hatayı almanızın nedeni 'T' is not convertible to 'T', aslında yeni bir , yönteminizde orijinal T ile hiç ilgili olmayan T .

İkinci hatayı almanızın nedeni, 'AnyObject' is not convertible to 'T'T için olası tüm değerlerin tüm sınıflar olmamasıdır. Bir örneğin AnyObject'e dönüştürülebilmesi için bir sınıf olması gerekir (bir yapı, numaralandırma vb. Olamaz).

En iyi seçeneğiniz, diziyi bağımsız değişken olarak kabul eden bir işlev yapmaktır:

func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) {
}

Veya orijinal diziyi değiştirmek yerine, bir kopya döndürerek yönteminizi daha güvenli ve yeniden kullanılabilir hale getirebilirsiniz:

func arrayRemovingObject<T : Equatable>(object: T, fromArray array: [T]) -> [T] {
}

Önermediğim bir alternatif olarak, dizide saklanan tür yöntemler şablonuna dönüştürülemiyorsa (bu eşittir) yönteminizin sessizce başarısız olmasını sağlayabilirsiniz. (Açıklık için, yöntemin şablon için T yerine U kullanıyorum):

extension Array {
    mutating func removeObject<U: Equatable>(object: U) {
        var index: Int?
        for (idx, objectToCompare) in enumerate(self) {
            if let to = objectToCompare as? U {
                if object == to {
                    index = idx
                }
            }
        }

        if(index != nil) {
            self.removeAtIndex(index!)
        }
    }
}

var list = [1,2,3]
list.removeObject(2) // Successfully removes 2 because types matched
list.removeObject("3") // fails silently to remove anything because the types don't match
list // [1, 3]

Düzenle Sessiz hatanın üstesinden gelmek için başarıyı bir bool olarak döndürebilirsiniz:

extension Array {
  mutating func removeObject<U: Equatable>(object: U) -> Bool {
    for (idx, objectToCompare) in self.enumerate() {  //in old swift use enumerate(self) 
      if let to = objectToCompare as? U {
        if object == to {
          self.removeAtIndex(idx)
          return true
        }
      }
    }
    return false
  }
}
var list = [1,2,3,2]
list.removeObject(2)
list
list.removeObject(2)
list

Cevabımı buradan kontrol edin: stackoverflow.com/a/24939242/458960 Neden findyöntemi kullanarak bu şekilde yapabilirim ?
Kardan Adam

Yönteminiz çalışma zamanı çökmelerine karşı hassastır. Benim fonksiyonumla, derleyici bunun olmasını engelleyecektir.
drewag

1
@Isuru Bu yöntem, Equatableprotokolü uygulayan herhangi bir nesne ile çalışır . UIView çok evet, UIViews ile çalışacak
drewag

4
Vay be, bir eleman kaldırmak için bir for döngüsü yazma, geri 90s için!
Zorayr

5
En son hızlıca. enumerate(self)düzeltmek zorundaself.enumerate()
TomSawyer

29

kısaca ve kısaca:

func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) 
{
    var index = find(array, object)
    array.removeAtIndex(index!)
}

2
Bu havalı. Tabii ki de olmadan yapılabilir inout. Bozulmadan bile inout, kişi kullanabilirdi, array = array.filter() { $0 != object }bence.
Dan Rosenstark

11
Sıfır olabilen sarılmamış indeks kullandığınızdan emin olun. "İf let ind = index {array.removeAtIndex (ind)}" olarak değiştirin
HotJard

17

Yukarıdakilerin hepsini okuduktan sonra, bence en iyi cevap:

func arrayRemovingObject<U: Equatable>(object: U, # fromArray:[U]) -> [U] {
  return fromArray.filter { return $0 != object }
}

Örneklem:

var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = arrayRemovingObject("Cat", fromArray:myArray )

Swift 2 (xcode 7b4) dizi uzantısı:

extension Array where Element: Equatable {  
  func arrayRemovingObject(object: Element) -> [Element] {  
    return filter { $0 != object }  
  }  
}  

Örneklem:

var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = myArray.arrayRemovingObject("Cat" )

Swift 3.1 güncellemesi

Swift 3.1 çıktı şimdi bu geri geldi. Aşağıda, kapsamlı, hızlı, mutasyona uğrayan ve varyantlar oluşturan bir uzantı bulunmaktadır.

extension Array where Element:Equatable {
    public mutating func remove(_ item:Element ) {
        var index = 0
        while index < self.count {
            if self[index] == item {
                self.remove(at: index)
            } else {
                index += 1
            }
        }
    }

    public func array( removing item:Element ) -> [Element] {
        var result = self
        result.remove( item )
        return result
    }
}

Örnekler:

// Mutation...
      var array1 = ["Cat", "Dog", "Turtle", "Cat", "Fish", "Cat"]
      array1.remove("Cat")
      print(array1) //  ["Dog", "Turtle", "Socks"]

// Creation...
      let array2 = ["Cat", "Dog", "Turtle", "Cat", "Fish", "Cat"]
      let array3 = array2.array(removing:"Cat")
      print(array3) // ["Dog", "Turtle", "Fish"]

bu dizinin tamamen yeni bir örneğini döndürmez mi?
pxpgraphics

Evet. Daha işlevsel bir tarz. YMMV.
Aralık

Bu durumda, filterişlevin sizin için zaten bu işlevi yerine getirmesi dışında, işlevsel stile katılma eğilimindeyim . Bu, yinelenen işlevsellik gibi görünüyor. Ama yine de iyi bir cevap:]
pxpgraphics

13

Protokol uzantıları ile bunu yapabilirsiniz,

extension Array where Element: Equatable {
    mutating func remove(object: Element) {
        if let index = indexOf({ $0 == object }) {
            removeAtIndex(index)
        }
    }
}

Sınıflar için aynı işlevsellik,

Hızlı 2

extension Array where Element: AnyObject {
    mutating func remove(object: Element) {
        if let index = indexOf({ $0 === object }) {
            removeAtIndex(index)
        }
    }
}

Hızlı 3

extension Array where Element: AnyObject {
    mutating func remove(object: Element) {
        if let index = index(where: { $0 === object }) {
             remove(at: index)
        }
    }
}

Fakat eğer bir sınıf Equatable uygularsa belirsiz hale gelir ve derleyici fırlatma hatası verir.


1
i alıyorumBinary operator '===' cannot be applied to two elements of type '_' and 'Element'
ayakkabıyı

6

Swift 2.0'da protokol uzantılarını kullanma

extension _ArrayType where Generator.Element : Equatable{
    mutating func removeObject(object : Self.Generator.Element) {
        while let index = self.indexOf(object){
            self.removeAtIndex(index)
        }
    }
}

4

filtrelemeyi kullanmaya ne dersiniz? aşağıdakiler [AnyObject] ile bile oldukça iyi çalışır.

import Foundation
extension Array {
    mutating func removeObject<T where T : Equatable>(obj: T) {
        self = self.filter({$0 as? T != obj})
    }

}

2

Kaldırılacak nesnenin genel türü, dizinin türüyle aynı olamayacağından, güvenli olmayan kullanıma sahip olmadan bir öğeyi bir diziden kaldırmanın başka bir olasılığı daha vardır. Optionals kullanmak da çok yavaş olduğu için mükemmel bir yol değildir. Bu nedenle, örneğin bir diziyi sıralarken daha önce kullanıldığı gibi bir kapak kullanabilirsiniz.

//removes the first item that is equal to the specified element
mutating func removeFirst(element: Element, equality: (Element, Element) -> Bool) -> Bool {
    for (index, item) in enumerate(self) {
        if equality(item, element) {
            self.removeAtIndex(index)
            return true
        }
    }
    return false
}

ArraySınıfı bu işlevle genişlettiğinizde , aşağıdakileri yaparak öğeleri kaldırabilirsiniz:

var array = ["Apple", "Banana", "Strawberry"]
array.removeFirst("Banana") { $0 == $1 } //Banana is now removed

Bununla birlikte, bir öğeyi yalnızca aynı bellek adresine sahipse bile kaldırabilirsiniz (yalnızca AnyObjectprotokole uyan sınıflar için ):

let date1 = NSDate()
let date2 = NSDate()
var array = [date1, date2]
array.removeFirst(NSDate()) { $0 === $1 } //won't do anything
array.removeFirst(date1) { $0 === $1 } //array now contains only 'date2'

İyi olan şu ki, karşılaştırılacak parametreyi belirtebilirsiniz. Örneğin, bir dizi diziniz olduğunda eşitlik kapanışını belirtebilirsiniz ve { $0.count == $1.count }kaldırılacak diziyle aynı boyuta sahip ilk dizi diziden kaldırılır.

Hatta işlevini kullanarak işlev çağrısını kısaltabilir mutating func removeFirst(equality: (Element) -> Bool) -> Bool, daha sonra if-değerlendirmesini değiştirebilir equality(item)ve işlevi array.removeFirst({ $0 == "Banana" })örneğin ile çağırabilirsiniz .


Yana ==bir fonksiyonudur, ayrıca her tür için böyle diyoruz ki uygular ==(vb dize, Int gibi):array.removeFirst("Banana", equality:==)
Aviel Brüt

@AvielGross Bu Swift 2'de yeni sanırım - isterseniz cevabı buna göre düzenlemekten çekinmeyin
borchero

2

Uzatmaya gerek yok:

var ra = [7, 2, 5, 5, 4, 5, 3, 4, 2]

print(ra)                           // [7, 2, 5, 5, 4, 5, 3, 4, 2]

ra.removeAll(where: { $0 == 5 })

print(ra)                           // [7, 2, 4, 3, 4, 2]

if let i = ra.firstIndex(of: 4) {
    ra.remove(at: i)
}

print(ra)                           // [7, 2, 3, 4, 2]

if let j = ra.lastIndex(of: 2) {
    ra.remove(at: j)
}

print(ra)                           // [7, 2, 3, 4]

1

indexOfA forveya yerine şunu kullanma enumerate:

extension Array where Element: Equatable {

   mutating func removeElement(element: Element) -> Element? {
      if let index = indexOf(element) {
         return removeAtIndex(index)
      }
      return nil
   }

   mutating func removeAllOccurrencesOfElement(element: Element) -> Int {
       var occurrences = 0
       while true {
          if let index = indexOf(element) {
             removeAtIndex(index)
             occurrences++
          } else {
             return occurrences
          }
       }
   }   
}

1

Belki de soruyu anlamadım.

Neden işe yaramadı?

import Foundation
extension Array where Element: Equatable {
    mutating func removeObject(object: Element) {
        if let index = self.firstIndex(of: object) {
            self.remove(at: index)
        }
    }
}

var testArray = [1,2,3,4,5,6,7,8,9,0]
testArray.removeObject(object: 6)
let newArray = testArray

var testArray2 = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
testArray2.removeObject(object: "6")
let newArray2 = testArray2

0

Sonunda aşağıdaki kodu ile sona erdi.

extension Array where Element: Equatable {

    mutating func remove<Element: Equatable>(item: Element) -> Array {
        self = self.filter { $0 as? Element != item }
        return self
    }

}

0

Ben bir for döngüsü dışında bir sayım uygulayarak [String:AnyObject]bir diziden kaldırmak başardı [[String:AnyObject]]beri endeksi temsil .findve .filteruyumlu değildir [String:AnyObject].

let additionValue = productHarvestChoices[trueIndex]["name"] as! String
var count = 0
for productHarvestChoice in productHarvestChoices {
  if productHarvestChoice["name"] as! String == additionValue {
    productHarvestChoices.removeAtIndex(count)
  }
  count = count + 1
}

-1

Swift 2'de uygulama:

extension Array {
  mutating func removeObject<T: Equatable>(object: T) -> Bool {
    var index: Int?
    for (idx, objectToCompare) in self.enumerate() {
      if let toCompare = objectToCompare as? T {
        if toCompare == object {
          index = idx
          break
        }
      }
    }
    if(index != nil) {
      self.removeAtIndex(index!)
      return true
    } else {
      return false
    }
  }
}

-4

Ben ile çalışmak için başardı:

extension Array {
    mutating func removeObject<T: Equatable>(object: T) {
        var index: Int?
        for (idx, objectToCompare) in enumerate(self) {
            let to = objectToCompare as T
            if object == to {
                index = idx
            }
        }

        if(index) {
            self.removeAtIndex(index!)
        }
    }
}

Karşılaştırma if(index)geçersiz
juanjo
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.