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

133
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'

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

  • 0
    Попробуйте удалить T where из вашего объявления вашего метода. Так что просто func removeObject<T: Equatable> . С этим вопросом связано: stackoverflow.com/questions/24091046/…
Теги:
arrays

14 ответов

172

Как и у 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)
        }
    }
}
  • 1
    Отлично, ты должен любить Свифта (2). Мне очень нравится, как со временем становится возможным больше вещей, а вещи упрощаются
  • 1
    Хороший момент, во многих отношениях тот факт, что ответ все еще технически правильный, просто больше не является идиоматическим, еще хуже - люди придут, прочитают ответ, думают, что бесплатная функция - верный способ ее решить, поскольку это высоко оцененный ответ , Довольно безобразный сценарий. Будет публиковать в мета.
Показать ещё 19 комментариев
68

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

ПРИМЕЧАНИЕ: с 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
  • 0
    Проверьте мой ответ здесь: stackoverflow.com/a/24939242/458960 Почему я могу сделать это таким образом, не используя метод find ?
  • 0
    Ваш метод подвержен сбоям во время выполнения. С моей функцией компилятор предотвратит это вообще.
Показать ещё 13 комментариев
29

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

func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) 
{
    var index = find(array, object)
    array.removeAtIndex(index!)
}
  • 2
    Это здорово. Конечно, это можно сделать и без inout . Даже с inout целы, можно было бы использовать, array = array.filter() { $0 != object } , я думаю.
  • 11
    Помните об использовании индекса принудительного развертывания, который может быть равен нулю. Измените на «если пусть ind = index {array.removeAtIndex (ind)}»
Показать ещё 1 комментарий
17

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

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"]
  • 0
    разве это не возвращает совершенно новый экземпляр массива?
  • 0
    Да. Это более функциональный стиль. YMMV.
Показать ещё 1 комментарий
13

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

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, он становится неоднозначным, и компилятор выдаёт ошибку.

  • 1
    я получаю Binary operator '===' cannot be applied to two elements of type '_' and 'Element'
7

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

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

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

}
  • 1
    это лучшее решение, которое сработало для меня.
2

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

//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" }), например.

  • 0
    Поскольку == является функцией, вы также можете вызывать ее так для любого типа, который реализует == (например, String, Int и т. Д.): array.removeFirst("Banana", equality:==)
  • 0
    @ AvielGross это новинка в Swift 2, я думаю - не стесняйтесь редактировать ответ соответственно, если вы хотите
1

Используя 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
          }
       }
   }   
}
0

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

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

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

Мне удалось удалить [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
}
0

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

extension Array where Element: Equatable {

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

}
-2

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

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

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!)
        }
    }
}
  • 0
    Сравнение if(index) неверно

Ещё вопросы

Сообщество Overcoder
Наверх
Меню