Расширение массива для удаления объекта по значению

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

Однако, я получаю сообщение об ошибке var index = find(self, object)

'T' не конвертируется в 'T'

Я также попытался с этой сигнатурой метода: func removeObject(object: AnyObject), однако, я получаю ту же ошибку:

'AnyObject' не конвертируется в 'T'

Каков правильный способ сделать это?

Ответ 1

Как и у Swift 2, это может быть достигнуто с помощью метода расширения протокола. removeObject() определяется как метод для всех типов, соответствующих до RangeReplaceableCollectionType (в частности, на Array), если элементы коллекции 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)
        }
    }
}

Пример:

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

Обновление для Swift 2/Xcode 7 beta 2: Как заметила скорость Airspeed Velocity в комментариях теперь фактически можно написать метод для общего типа, который более ограничивает шаблон, поэтому метод теперь можно было бы фактически определить как расширение Array:

extension Array where Element : Equatable {

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

Расширение протокола по-прежнему имеет то преимущество, что оно применимо к более широкий набор типов.

Обновление для 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)
        }
    }
}

Ответ 2

Вы не можете написать метод для общего типа, который более ограничивает шаблон.

ПРИМЕЧАНИЕ: с Swift 2.0 вы можете теперь писать методы, более ограничивающие шаблон. Если вы обновили код до версии 2.0, см. Другие ответы ниже для новых вариантов реализации, используя расширения.

Причина, по которой вы получаете сообщение об ошибке 'T' is not convertible to 'T', состоит в том, что вы фактически определяете новый T в своем методе, который вообще не связан с исходным T. Если вы хотите использовать T в своем метод, вы можете сделать это, не указав его в своем методе.

Причина, по которой вы получаете вторую ошибку 'AnyObject' is not convertible to 'T', состоит в том, что все возможные значения для T - это не все классы. Для экземпляра, который должен быть преобразован в AnyObject, он должен быть классом (он не может быть структурой, перечислением и т.д.).

Лучше всего сделать его функцией, которая принимает массив в качестве аргумента:

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

Или вместо изменения исходного массива вы можете сделать свой метод более потокобезопасным и повторно использовать, возвращая копию:

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

В качестве альтернативы, которую я не рекомендую, вы можете заставить свой метод терпеть неудачу, если тип, хранящийся в массиве, не может быть преобразован в шаблон методов (что равносильно). (Для ясности я использую U вместо T для шаблона метода):

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]

Изменить. Чтобы преодолеть бесшумный сбой, вы можете вернуть успех как 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

Ответ 3

кратко и кратко:

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

Ответ 4

После прочтения всего вышеизложенного, на мой взгляд, лучший ответ:

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

Пример:

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

Расширение массива Swift 2 (xcode 7b4):

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

Пример:

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

Обновление Swift 3.1

Вернулся к этому теперь, когда Swift 3.1 вышел. Ниже приведено расширение, которое предоставляет исчерпывающие, быстрые, мутирующие и создающие варианты.

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

Образцы:

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

Ответ 5

С расширением протокола вы можете сделать это,

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

Такая же функциональность для классов,

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

Но если класс реализует Equatable, он становится неоднозначным, и компилятор выдаёт ошибку.

Ответ 6

С использованием расширений протокола в 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

как насчет фильтрации? следующее работает достаточно хорошо даже с [AnyObject].

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

}

Ответ 8

Существует еще одна возможность удаления элемента из массива без возможного небезопасного использования, поскольку общий тип объекта для удаления не может быть таким же, как тип массива. Использование опций также не является идеальным способом, поскольку они очень медленны. Поэтому вы можете использовать закрытие, как оно уже используется при сортировке массива, например.

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

Когда вы расширяете класс Array с помощью этой функции, вы можете удалить элементы, выполнив следующие действия:

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

Однако вы даже можете удалить элемент только в том случае, если он имеет одинаковый адрес памяти (конечно, только для классов, соответствующих протоколу AnyObject):

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'

Хорошо, что вы можете указать параметр для сравнения. Например, если у вас есть массив массивов, вы можете указать закрытие равенства как { $0.count == $1.count }, и первый массив, имеющий тот же размер, что и тот, который удаляется, удаляется из массива.

Вы даже можете сократить вызов функции, указав функцию mutating func removeFirst(equality: (Element) -> Bool) -> Bool, затем замените if-оценку на equality(item) и вызовите функцию array.removeFirst({ $0 == "Banana" }), например.

Ответ 9

Используя indexOf вместо for или 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
          }
       }
   }   
}

Ответ 10

Наконец я закончил с кодом.

extension Array where Element: Equatable {

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

}

Ответ 11

Мне удалось удалить [String:AnyObject] из массива [[String:AnyObject]], выполнив счет за пределами цикла for для представления индекса, так как .find и .filter не совместимы с [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
}

Ответ 12

Возможно, я не понял этого вопроса.

Почему бы не работать?

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

Ответ 13

Реализация в 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
    }
  }
}

Ответ 14

Мне удалось заставить его работать с:

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