ios - for - swift set
Extensión de matriz para eliminar objeto por valor (13)
extension Array {
func removeObject<T where T : Equatable>(object: T) {
var index = find(self, object)
self.removeAtIndex(index)
}
}
Sin embargo, obtengo un error en var index = find(self, object)
''T'' no es convertible a ''T''
También probé con este método signature: func removeObject(object: AnyObject)
, sin embargo, obtengo el mismo error:
''AnyObject'' no es convertible a ''T''
¿Cuál es la forma apropiada de hacer esto?
¿Qué tal usar el filtrado? lo siguiente funciona bastante bien incluso con [AnyObject].
import Foundation
extension Array {
mutating func removeObject<T where T : Equatable>(obj: T) {
self = self.filter({$0 as? T != obj})
}
}
A partir de Swift 2 , esto se puede lograr con un método de extensión de protocolo . removeObject()
se define como un método en todos los tipos que se ajustan a RangeReplaceableCollectionType
(en particular en Array
) si los elementos de la colección son 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)
}
}
}
Ejemplo:
var ar = [1, 2, 3, 2]
ar.removeObject(2)
print(ar) // [1, 3, 2]
Actualización para Swift 2 / Xcode 7 beta 2: Como Airspeed Velocity notó en los comentarios, ahora es posible escribir un método en un tipo genérico que sea más restrictivo en la plantilla, por lo que el método ahora podría definirse como una extensión de Array
:
extension Array where Element : Equatable {
// ... same method as above ...
}
La extensión de protocolo todavía tiene la ventaja de ser aplicable a un conjunto de tipos más grande.
Actualización para 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)
}
}
}
Con el uso de extensiones de protocolo en 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)
}
}
}
Con las extensiones de protocolo puede hacer esto,
extension Array where Element: Equatable {
mutating func remove(object: Element) {
if let index = indexOf({ $0 == object }) {
removeAtIndex(index)
}
}
}
La misma funcionalidad para las clases,
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)
}
}
}
Pero si una clase implementa Equatable se vuelve ambigua y el compilador arroja un error.
Después de leer todo lo anterior, en mi opinión la mejor respuesta es:
func arrayRemovingObject<U: Equatable>(object: U, # fromArray:[U]) -> [U] {
return fromArray.filter { return $0 != object }
}
Muestra:
var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = arrayRemovingObject("Cat", fromArray:myArray )
Extensión de matriz Swift 2 (xcode 7b4):
extension Array where Element: Equatable {
func arrayRemovingObject(object: Element) -> [Element] {
return filter { $0 != object }
}
}
Muestra:
var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = myArray.arrayRemovingObject("Cat" )
Actualización de Swift 3.1
Volvió a esto ahora que Swift 3.1 está fuera. A continuación hay una extensión que proporciona variantes exhaustivas, rápidas, mutantes y creadoras.
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
}
}
Muestras:
// 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"]
Existe otra posibilidad de eliminar un elemento de una matriz sin tener un posible uso inseguro, ya que el tipo genérico del objeto para eliminar no puede ser el mismo que el tipo de la matriz. Usar opcionales tampoco es la manera perfecta de ir, ya que son muy lentos. Por lo tanto, podría utilizar un cierre como ya se usa al ordenar una matriz, por ejemplo.
//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
}
Cuando extiende la clase Array
con esta función, puede eliminar elementos haciendo lo siguiente:
var array = ["Apple", "Banana", "Strawberry"]
array.removeFirst("Banana") { $0 == $1 } //Banana is now removed
Sin embargo, incluso podría eliminar un elemento solo si tiene la misma dirección de memoria (solo para las clases que se ajustan al protocolo AnyObject
, por supuesto):
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''
Lo bueno es que puedes especificar el parámetro para comparar. Por ejemplo, cuando tiene una matriz de matrices, puede especificar el cierre de igualdad como { $0.count == $1.count }
y la primera matriz que tiene el mismo tamaño que la que se elimina se elimina de la matriz.
Incluso podría acortar la llamada de función teniendo la función como mutating func removeFirst(equality: (Element) -> Bool) -> Bool
, luego reemplace if-evaluation con equality(item)
y llame a la función mediante array.removeFirst({ $0 == "Banana" })
por ejemplo.
Finalmente terminé con el siguiente código.
extension Array where Element: Equatable {
mutating func remove<Element: Equatable>(item: Element) -> Array {
self = self.filter { $0 as? Element != item }
return self
}
}
Implementación en 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
}
}
}
No puede escribir un método en un tipo genérico que sea más restrictivo en la plantilla.
NOTA : a partir de Swift 2.0, ahora puede escribir métodos que son más restrictivos en la plantilla. Si ha actualizado su código a 2.0, consulte otras respuestas más abajo para conocer las nuevas opciones para implementar esto mediante el uso de extensiones.
La razón por la que obtiene el error ''T'' is not convertible to ''T''
es que realmente está definiendo una nueva T en su método que no está relacionada en absoluto con la T. original. Si desea usar T en su método, usted puede hacerlo sin especificarlo en su método.
El motivo por el que obtiene el segundo error ''AnyObject'' is not convertible to ''T''
es que todos los valores posibles para T no son todas las clases. Para que una instancia se convierta en AnyObject, debe ser una clase (no puede ser una estructura, una enumeración, etc.).
Su mejor opción es convertirlo en una función que acepte la matriz como argumento:
func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) {
}
O bien, en lugar de modificar la matriz original, puede hacer que su método sea más seguro y reutilizable mediante la devolución de una copia:
func arrayRemovingObject<T : Equatable>(object: T, fromArray array: [T]) -> [T] {
}
Como alternativa que no recomiendo, puede hacer que su método falle silenciosamente si el tipo almacenado en la matriz no se puede convertir a la plantilla de métodos (que es equitable). (Para mayor claridad, estoy usando U en lugar de T para la plantilla del método):
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]
Editar Para superar la falla silenciosa puede devolver el éxito como un 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
Pude hacerlo funcionar con:
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!)
}
}
}
Usando indexOf
lugar de a for
o 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
}
}
}
}
breve y concisamente
func removeObject<T : Equatable>(object: T, inout fromArray array: [T])
{
var index = find(array, object)
array.removeAtIndex(index!)
}
[String:AnyObject]
eliminar un [String:AnyObject]
de una matriz [[String:AnyObject]]
implementando un conteo fuera de un ciclo for para representar el índice, ya que .find
y .filter
no son compatibles con [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
}