web-dev-qa-db-de.com

Array-Erweiterung zum Entfernen des Objekts nach Wert

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

Ich bekomme jedoch einen Fehler bei var index = find(self, object)

'T' kann nicht in 'T' konvertiert werden

Ich habe auch mit dieser Methodensignatur versucht: func removeObject(object: AnyObject), ich bekomme jedoch den gleichen Fehler:

'AnyObject' kann nicht in 'T' konvertiert werden

Was ist der richtige Weg, dies zu tun?

134
Snowman

Ab Swift 2 kann dies mit einer Protokoll-Erweiterungsmethode erreicht werden. .removeObject() ist als Methode auf allen Typen definiert, die konform sindto RangeReplaceableCollectionType (insbesondere auf Array) Wenn Die Elemente der Auflistung sind 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)
        }
    }
}

Beispiel:

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

Update für Swift 2/Xcode 7 Beta 2: Wie Airspeed Velocity in den Kommentaren bemerkt hat, ist es jetzt tatsächlich möglich, eine Methode für einen generischen Typ zu schreiben, der die Vorlage einschränkt, also die Methode könnte jetzt tatsächlich als Erweiterung von Array definiert werden:

extension Array where Element : Equatable {

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

Die Protokollerweiterung hat immer noch den Vorteil, dass sie auf einen größeren Satz von Typen anwendbar ist.

Update für 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)
        }
    }
}
164
Martin R

Sie können keine Methode für einen generischen Typ schreiben, der die Vorlage einschränkt.

NOTE: Ab Swift 2.0 können Sie jetzt Methoden schreiben, die are für die Vorlage restriktiver sind. Wenn Sie Ihren Code auf 2.0 aktualisiert haben, finden Sie in den weiter unten aufgeführten Antworten weitere Optionen, um dies mithilfe von Erweiterungen zu implementieren.

Sie erhalten den Fehler 'T' is not convertible to 'T', weil Sie in Ihrer Methode tatsächlich ein new T definieren, das überhaupt nicht mit dem ursprünglichen T zusammenhängt. Wenn Sie T in Ihrer Methode verwenden möchten, können Sie dies ohne Angabe von Angaben tun es auf deine Methode.

Der Grund, dass Sie den zweiten Fehler 'AnyObject' is not convertible to 'T' erhalten, ist, dass nicht alle möglichen Werte für T Klassen sind. Damit eine Instanz in AnyObject konvertiert werden kann, muss es sich um eine Klasse handeln (es darf keine Struktur, kein Enum usw. sein).

Ihre beste Wette ist, es eine Funktion zu machen, die das Array als Argument akzeptiert:

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

Anstatt das ursprüngliche Array zu ändern, können Sie Ihre Methode sicherer und wiederverwendbarer machen, indem Sie eine Kopie zurückgeben:

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

Als Alternative, die ich nicht empfehle, können Sie Ihre Methode im Ruhezustand ausfallen lassen, wenn der im Array gespeicherte Typ nicht in die Methodenvorlage konvertiert werden kann (das ist gleichwertig). (Zur besseren Übersicht benutze ich U anstelle von T für die Vorlage der Methode):

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]

Bearbeiten Um den stillen Fehler zu überwinden, können Sie den Erfolg als bool zurückgeben:

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
65
drewag

kurz und prägnant:

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

Nachdem ich all das gelesen habe, ist die beste Antwort meines Erachtens:

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

Probe:

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

Swift 2 (xcode 7b4) Array-Erweiterung:

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

Probe: 

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

Swift 3.1 Update

Kam zurück, dass jetzt Swift 3.1 raus ist. Nachfolgend finden Sie eine Erweiterung, die erschöpfende, schnelle, mutierende und erstellende Varianten bietet. 

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

Proben: 

// 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"]
17
rosstulloch

Mit Protokollerweiterungen können Sie dies tun,

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

Gleiche Funktionalität für Klassen 

Swift 2

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

Swift 3

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

Wenn eine Klasse Equatable implementiert, wird sie mehrdeutig und der Compiler gibt einen Fehler aus.

13

Mit Protokollerweiterungen in 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)
        }
    }
}
7
ogantopkaya

was ist mit dem Filtern? Folgendes funktioniert auch mit [AnyObject] recht gut.

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

}
4
valvoline

Es gibt eine weitere Möglichkeit, ein Element aus einem Array zu entfernen, ohne dass es zu einer unsicheren Verwendung kommt, da der generische Typ des zu entfernenden Objekts nicht mit dem Typ des Arrays identisch sein kann. Die Verwendung von Optionen ist auch nicht der perfekte Weg, da sie sehr langsam sind. Sie können also einen Abschluss verwenden, wie er beispielsweise beim Sortieren eines Arrays bereits verwendet wird.

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

Wenn Sie die Klasse Array mit dieser Funktion erweitern, können Sie Elemente entfernen, indem Sie folgende Schritte ausführen:

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

Sie können ein Element jedoch nur dann entfernen, wenn es dieselbe Speicheradresse hat (natürlich nur für Klassen, die dem Protokoll AnyObject entsprechen):

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'

Das Gute ist, dass Sie den zu vergleichenden Parameter angeben können. Wenn Sie beispielsweise über ein Array von Arrays verfügen, können Sie den Gleichheitsabschluss als { $0.count == $1.count } angeben, und das erste Array, das dieselbe Größe wie das zu entfernende Array hat, wird aus dem Array entfernt.

Sie können den Funktionsaufruf sogar verkürzen, indem Sie die Funktion als mutating func removeFirst(equality: (Element) -> Bool) -> Bool verwenden, dann die if-Auswertung durch equality(item) ersetzen und die Funktion beispielsweise mit array.removeFirst({ $0 == "Banana" }) aufrufen.

2
borchero

Verwendung von indexOf anstelle von for oder 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
juanjo

Es ist mir gelungen, einen [String:AnyObject] aus einem Array [[String:AnyObject]] zu entfernen, indem ich einen Zähler außerhalb einer for-Schleife implementiert, um den Index darzustellen, da .find und .filter nicht mit [String:AnyObject] kompatibel sind. 

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

Vielleicht habe ich die Frage nicht verstanden.

Warum sollte das nicht funktionieren?

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

Ich endete schließlich mit folgendem Code.

extension Array where Element: Equatable {

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

}
0
Kaz Yoshikawa