Array ekstensi untuk menghapus objek berdasarkan nilai

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

Namun, saya mendapatkan kesalahan var index = find(self, object)

'T' tidak dapat dikonversi menjadi 'T'

Saya juga mencoba dengan metode tanda tangan ini:, func removeObject(object: AnyObject)namun, saya mendapatkan kesalahan yang sama:

'AnyObject' tidak dapat dikonversi menjadi 'T'

Apa cara yang tepat untuk melakukan ini?

Manusia salju
sumber
Coba hapus T wheredari deklarasi metode Anda. Jadi adil saja func removeObject<T: Equatable>. Pertanyaan ini terkait: stackoverflow.com/questions/24091046/…
ahruss

Jawaban:

165

Pada Swift 2 , ini dapat dicapai dengan metode ekstensi protokol . removeObject()didefinisikan sebagai metode pada semua jenis yang sesuai dengan RangeReplaceableCollectionType(khususnya pada Array) jika elemen koleksi adalah Equatable:

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

Contoh:

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

Pembaruan untuk Swift 2 / Xcode 7 beta 2: Seperti yang diperlihatkan oleh Airspeed Velocity dalam komentar, sekarang mungkin untuk menulis metode pada tipe generik yang lebih ketat pada templat, sehingga metode tersebut sekarang dapat didefinisikan sebagai ekstensi dari Array:

extension Array where Element : Equatable {

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

Ekstensi protokol masih memiliki keuntungan untuk diterapkan pada serangkaian tipe yang lebih besar.

Pembaruan untuk Swift 3:

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)
        }
    }
}
Martin R
sumber
1
Sempurna, Anda harus mencintai Swift (2). Saya sangat suka bagaimana dengan waktu semakin banyak hal menjadi mungkin dan hal-hal menjadi lebih sederhana
Kametrixom
1
Poin yang baik, dalam banyak hal fakta bahwa jawabannya masih benar secara teknis, tidak lagi idiomatis, bahkan lebih buruk - orang akan datang, membaca jawabannya, berpikir fungsi bebas adalah cara yang tepat untuk menyelesaikannya karena itu adalah jawaban yang berperingkat tinggi . Skenario yang cukup jelek. Akan memposting ke meta.
Airspeed Velocity
1
@ AirspeedVelocity: Wow, saya melewatkan itu. Apakah itu tercakup dalam catatan rilis?
Martin R
1
Jika Anda ingin fungsi yang sama dengan ObjC (yaitu menghapus semua objek yang cocok, bukan hanya objek 1), Anda dapat mengubah "jika" menjadi "sementara"
powertoold
2
Versi Swift 3 hebat, tapi saya akan mengubah nama deklarasi sedikit remove(object: Element)untuk memenuhi pedoman desain API Swift dan menghindari verbositas. Saya telah mengirimkan hasil edit yang mencerminkan hal ini.
swiftcode
66

Anda tidak dapat menulis metode pada tipe generik yang lebih membatasi pada templat.

CATATAN : pada Swift 2.0, kini Anda dapat menulis metode yang berada lebih ketat pada template. Jika Anda telah memutakhirkan kode Anda ke 2.0, lihat jawaban lain lebih jauh ke bawah untuk opsi baru untuk mengimplementasikan ekstensi ini menggunakan.

Alasan Anda mendapatkan kesalahan 'T' is not convertible to 'T'adalah bahwa Anda sebenarnya mendefinisikan yang baru T dalam metode Anda yang sama sekali tidak terkait dengan T. asli. Jika Anda ingin menggunakan T dalam metode Anda, Anda dapat melakukannya tanpa menentukannya pada metode Anda.

Alasan Anda mendapatkan kesalahan kedua 'AnyObject' is not convertible to 'T'adalah bahwa semua nilai yang mungkin untuk T tidak semua kelas. Untuk instance yang akan dikonversi ke AnyObject, itu harus kelas (itu tidak bisa menjadi struct, enum, dll.).

Taruhan terbaik Anda adalah menjadikannya fungsi yang menerima array sebagai argumen:

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

Atau alih-alih memodifikasi array asli, Anda dapat membuat metode Anda lebih aman dan dapat digunakan kembali dengan mengembalikan salinan:

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

Sebagai alternatif yang saya tidak rekomendasikan, Anda dapat gagal metode Anda secara diam-diam jika tipe yang disimpan dalam array tidak dapat dikonversi ke templat metode (yang setara). (Untuk kejelasan, saya menggunakan U alih-alih T untuk templat metode ini):

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]

Sunting Untuk mengatasi kegagalan diam, Anda dapat mengembalikan kesuksesan sebagai bool:

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
drewag
sumber
Lihat jawaban saya di sini: stackoverflow.com/a/24939242/458960 Mengapa saya bisa melakukannya dengan cara ini dan tidak menggunakan findmetode ini?
Snowman
Metode Anda rentan terhadap crash runtime. Dengan fungsi saya, kompiler akan mencegah hal itu terjadi sama sekali.
drewag
1
@Isuru Metode ini berfungsi dengan objek apa pun yang mengimplementasikan Equatableprotokol. UIView melakukannya ya itu akan bekerja dengan UIViews
drewag
4
Wow, menulis loop for untuk menghapus elemen, kembali ke tahun 90-an!
Zorayr
5
Dalam deretan terbaru. enumerate(self)harus memperbaiki keself.enumerate()
TomSawyer
29

secara singkat dan singkat:

func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) 
{
    var index = find(array, object)
    array.removeAtIndex(index!)
}
János
sumber
2
Ini keren. Tentu saja itu bisa dilakukan tanpa itu inoutjuga. Bahkan dengan inoututuh, orang bisa menggunakan array = array.filter() { $0 != object },, saya pikir.
Dan Rosenstark
11
Berhati-hatilah dalam menggunakan indeks force unrapped, yang mungkin nol. Ubah ke "jika biarkan ind = index {array.removeAtIndex (ind)}"
HotJard
17

Setelah membaca semua hal di atas, menurut saya jawaban terbaik adalah:

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

Sampel:

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

Ekstensi array Swift 2 (xcode 7b4):

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

Sampel:

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

Pembaruan Swift 3.1

Kembali ke ini sekarang karena Swift 3.1 sedang tidak aktif. Di bawah ini adalah ekstensi yang menyediakan varian lengkap, cepat, bermutasi dan membuat.

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

Sampel:

// 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"]
Desember
sumber
bukankah ini mengembalikan contoh array yang sama sekali baru?
pxpgraphics
Iya. Ini gaya yang lebih fungsional. YMMV.
Desember
Saya cenderung setuju dengan gaya fungsional, kecuali, dalam hal ini, ketika filterfungsi sudah menangani fungsi itu untuk Anda. Ini tampaknya menduplikasi fungsi. Namun jawaban yang bagus:]
pxpgraphics
13

Dengan ekstensi protokol Anda dapat melakukan ini,

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

Fungsionalitas yang sama untuk kelas,

Cepat 2

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

Cepat 3

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

Tetapi jika kelas mengimplementasikan Equatable itu menjadi ambigu dan kompiler memberikan kesalahan melempar.

Sri Krishna Paritala
sumber
1
Saya mendapatkanBinary operator '===' cannot be applied to two elements of type '_' and 'Element'
sepatu
6

Dengan menggunakan ekstensi protokol di swift 2.0

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

bagaimana dengan menggunakan pemfilteran? berikut ini bekerja dengan cukup baik bahkan dengan [AnyObject].

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

}
valvoline
sumber
2

Ada kemungkinan lain untuk menghapus item dari array tanpa memiliki kemungkinan penggunaan yang tidak aman, karena tipe generik objek yang akan dihapus tidak boleh sama dengan jenis array. Menggunakan opsional juga bukan cara sempurna untuk digunakan karena sangat lambat. Karena itu Anda bisa menggunakan penutupan seperti itu sudah digunakan saat mengurutkan array misalnya.

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

Ketika Anda memperluas Arraykelas dengan fungsi ini, Anda dapat menghapus elemen dengan melakukan hal berikut:

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

Namun Anda bahkan dapat menghapus elemen hanya jika memiliki alamat memori yang sama (tentu saja untuk kelas yang sesuai dengan AnyObjectprotokol):

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'

Hal yang baik adalah, Anda dapat menentukan parameter untuk dibandingkan. Misalnya ketika Anda memiliki array array, Anda dapat menentukan penutupan kesetaraan sebagai{ $0.count == $1.count } dan array pertama memiliki ukuran yang sama dengan yang akan dihapus dihapus dari array.

Anda bahkan dapat mempersingkat pemanggilan fungsi dengan memiliki fungsi sebagai mutating func removeFirst(equality: (Element) -> Bool) -> Bool, lalu mengganti if-evaluation dengan equality(item)dan memanggil fungsi dengan array.removeFirst({ $0 == "Banana" })misalnya.

borchero
sumber
Karena ==merupakan fungsi, Anda juga dapat menyebutnya seperti ini untuk semua jenis yang mengimplementasikan ==(seperti String, Int, dll.):array.removeFirst("Banana", equality:==)
Aviel Gross
@ AviviGross ini baru di Swift 2 saya pikir - jangan ragu untuk mengedit jawaban sesuai jika Anda mau
borchero
2

Tidak perlu diperpanjang:

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]
Roi Zakai
sumber
1

Menggunakan indexOfbukannya a foratau 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
          }
       }
   }   
}
juanjo
sumber
1

Mungkin saya tidak mengerti pertanyaannya.

Mengapa ini tidak berhasil?

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
Chris Marshall
sumber
0

Saya akhirnya berakhir dengan kode berikut.

extension Array where Element: Equatable {

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

}
Kaz Yoshikawa
sumber
0

Saya berhasil menghapus [String:AnyObject]dari array [[String:AnyObject]]dengan menerapkan hitungan di luar loop untuk mewakili indeks sejak .finddan .filtertidak kompatibel dengan [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
}
Tobias Brysiewicz
sumber
-1

Implementasi dalam Swift 2:

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
    }
  }
}
wcharysz
sumber
-4

Saya bisa membuatnya bekerja dengan:

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!)
        }
    }
}
Manusia salju
sumber
Perbandingannya if(index)tidak valid
juanjo