Удаление объекта из массива в Swift 3

93

В моем приложении я добавил один объект в массив при выборе ячейки и отменил выбор и удалил объект при повторном выборе ячейки. Я использовал этот код, но дал мне ошибку.

extension Array {
    func indexOfObject(object : AnyObject) -> NSInteger {
        return (self as NSArray).indexOfObject(object)
    }

    mutating func removeObject(object : AnyObject) {
        for var index = self.indexOfObject(object); index != NSNotFound; index = self.indexOfObject(object) {
            self.removeAtIndex(index)
        }
    }
}

class MyViewController: UITableViewController {
    var arrContacts: [Any] = []
    var contacts: [Any] = []

    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        arrContacts.removeObject(contacts[indexPath.row])
    }
}

Это дает мне 2 такие ошибки:

C-style for statement has been removed in Swift 3
Value of type '[Any]' has no member 'removeObject'
Камлеш Шингарахия
источник
Вы можете использовать, Set<Contact>а не массив. Не могли бы вы предоставить дополнительную информацию о вашем контактном объекте? Если вы сделали это сами, вам нужно, чтобы он соответствовал Hashableи Equatableдля того, чтобы поместить его в набор
Paulw11

Ответы:

166

Swift, эквивалентный NSMutableArrays removeObject:

var array = ["alpha", "beta", "gamma"]

if let index = array.firstIndex(of: "beta") {
    array.remove(at: index)
}

если объекты уникальны . Совершенно не нужно преобразовывать NSArrayи использоватьindexOfObject:

API index(of:также работает, но это вызывает ненужное неявное преобразование моста в NSArray.

Если есть несколько экземпляров одного и того же объекта, используйте filter. Однако в таких случаях, как массивы источников данных, где индекс связан с конкретным объектом firstIndex(of, предпочтительнее, потому что он быстрее, чем filter.

Обновить:

В Swift 4.2+ вы можете удалить одно или несколько вхождений betaс помощью removeAll(where:):

array.removeAll{$0 == "beta"}
вадиан
источник
34
Это лучший ответ, но глупо не иметь remove (object: "beta").
zeeple 01
5
Я думаю, .index(of: доступен только в том случае, если коллекция содержит Equatableтипы.
Адам Уэйт
@AdamWaite Да, но это также относится к типам Foundation.
vadian
Это неправильно, а что, если у вас более одной «бета-версии»? Это работает, только если массив не содержит более одного вхождения. Правильный ответ - использовать фильтр или запустить этот ответ через некоторое время,while let index = array.index(of: "beta") { array.remove(at: index) }
Хуанказалла
@juancazalla Вы правы, но в случае, если массив может содержать более одного экземпляра, используйте filterрешение. Но если объекты уникальны, используйте всегда, index(ofпотому что они намного производительнее, чемfilter
vadian
72
var a = ["one", "two", "three", "four", "five"]

// Remove/filter item with value 'three'
a = a.filter { $0 != "three" }
nyxee
источник
7
Это правильное решение Swift, в котором используются синтаксические особенности языка.
Майкл
1
Что, если предмет - это предмет?
TomSawyer
@TomSawyer, чтобы отфильтровать объект, используйте $ 0! ==
Майк Таверн
25

Для Swift 3 вы можете использовать index (where :) и включить закрытие, которое сравнивает объект в массиве ($ 0) с тем, что вы ищете.

var array = ["alpha", "beta", "gamma"]
if let index = array.index(where: {$0 == "beta"}) {
  array.remove(at: index)
}
Марк Семсель
источник
это сработает, если я хочу удалить несколько объектов? как (где: {$ 0 == "beta" || $ 0 == "gamma"})
Иршад Мохамед
16

Еще одно приятное и полезное решение - создать такое расширение:

extension Array where Element: Equatable {

    @discardableResult mutating func remove(object: Element) -> Bool {
        if let index = index(of: object) {
            self.remove(at: index)
            return true
        }
        return false
    }

    @discardableResult mutating func remove(where predicate: (Array.Iterator.Element) -> Bool) -> Bool {
        if let index = self.index(where: { (element) -> Bool in
            return predicate(element)
        }) {
            self.remove(at: index)
            return true
        }
        return false
    }

}

Таким образом, если у вас есть массив с настраиваемыми объектами:

let obj1 = MyObject(id: 1)
let obj2 = MyObject(id: 2)
var array: [MyObject] = [obj1, obj2]

array.remove(where: { (obj) -> Bool in
    return obj.id == 1
})
// OR
array.remove(object: obj2) 
Лука Даванцо
источник
1
Это работает, только если массив не содержит более одного вхождения. Правильный ответ - использовать фильтр или запустить этот ответ через некоторое время. Как пользователь этого расширения, я ожидаю, что оно удалит все вхождения, а не только одно
juancazalla 01
Это хорошо, но должно быть, remove(element: Element)потому что в Array вы можете хранить также такие типы, как Int, Double - они не являются объектами.
Радек Вильчак
8

В Swift 5 используйте это Extension:

extension Array where Element: Equatable{
    mutating func remove (element: Element) {
        if let i = self.firstIndex(of: element) {
            self.remove(at: i)
        }
    }
}

пример:

var array = ["alpha", "beta", "gamma"]
array.remove(element: "beta")

В Swift 3 используйте это Extension:

extension Array where Element: Equatable{
    mutating func remove (element: Element) {
        if let i = self.index(of: element) {
            self.remove(at: i)
        }
    }
}

пример:

var array = ["alpha", "beta", "gamma"]
array.remove(element: "beta")
Мохсеназм
источник
6
  1. for var index = self.indexOfObject(object); index != NSNotFound; index = self.indexOfObject(object) предназначен для цикла в стиле C и был удален

  2. Измените свой код на что-то вроде этого, чтобы удалить все похожие объекты, если они зациклились:

    let indexes = arrContacts.enumerated().filter { $0.element == contacts[indexPath.row] }.map{ $0.offset }
    for index in indexes.reversed() {
       arrContacts.remove(at: index)
    }
    
Tj3n
источник
enumerated -> filter -> map and remove (at) - умное решение. Порекомендуйте этот
Ryan X
4

Swift 4

var students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]

if let index = students.firstIndex(where: { $0.hasPrefix("A") }) {
   students.remove(at: index)
}
Сергей Ди
источник
3

Правильное и рабочее однострочное решение для удаления уникального объекта (с именем «objectToRemove») из массива этих объектов (с именем «array») в Swift 3:

if let index = array.enumerated().filter( { $0.element === objectToRemove }).map({ $0.offset }).first {
   array.remove(at: index)
}
Joerg
источник
1

Попробуйте это в Swift 3

array.remove(at: Index)

Вместо того

array.removeAtIndex(index)

Обновить

"Declaration is only valid at file scope".

Убедитесь, что объект находится в области видимости. Вы можете указать "внутреннюю" область видимости, которая используется по умолчанию.

index(of:<Object>) чтобы работать, класс должен соответствовать Equatable

Дили
источник
1

В Swift 3 и 4

var array = ["a", "b", "c", "d", "e", "f"]

for (index, element) in array.enumerated().reversed() {
    array.remove(at: index)
}

В Swift 4.2 вы можете использовать более продвинутый подход (более быстрый и эффективный с точки зрения памяти)

array.removeAll(where: { $0 == "c" })

вместо того

array = array.filter { !$0.hasPrefix("c") }

Подробнее здесь

yoAlex5
источник
1

Расширение для массива, чтобы сделать это легко и позволить цепочку для Swift 4.2 и выше:

public extension Array where Element: Equatable {
    @discardableResult
    public mutating func remove(_ item: Element) -> Array {
        if let index = firstIndex(where: { item == $0 }) {
            remove(at: index)
        }
        return self
    }

    @discardableResult
    public mutating func removeAll(_ item: Element) -> Array {
        removeAll(where: { item == $0 })
        return self
    }
}
Ренетик
источник
Ярлыки аргументов '(где :)' не соответствуют ни одной доступной перегрузке
jeet.chanchawat
1
@ jeet.chanchawat ну наверное другая быстрая версия тогда ... Ой, этот вопрос был для 3? Ну, я думаю, у меня было 4.2 на момент написания, но сейчас не помню, проверю позже, у меня определенно сработало
Ренетик
0

Это официальный ответ для поиска индекса конкретного объекта, после чего вы можете легко удалить любой объект, используя этот индекс:

var students = ["Ben", "Ivy", "Jordell", "Maxime"]
if let i = students.firstIndex(of: "Maxime") {
     // students[i] = "Max"
     students.remove(at: i)
}
print(students)
// Prints ["Ben", "Ivy", "Jordell"]

Вот ссылка: https://developer.apple.com/documentation/swift/array/2994720-firstindex

Махеш Челия
источник
0

Это то, что я использовал (Swift 5) ...

    extension Array where Element:Equatable
    {
        @discardableResult
        mutating func removeFirst(_ item:Any ) -> Any? {
            for index in 0..<self.count {
                if(item as? Element == self[index]) {
                    return self.remove(at: index)
                }
            }
            return nil
        }
        @discardableResult
        mutating func removeLast(_ item:Any ) -> Any? {
            var index = self.count-1
            while index >= 0 {
                if(item as? Element == self[index]) {
                    return self.remove(at: index)
                }
                index -= 1
            }
            return nil
        }
    }

    var arrContacts:[String] = ["A","B","D","C","B","D"]
    var contacts: [Any] = ["B","D"]
    print(arrContacts)
    var index = 1
    arrContacts.removeFirst(contacts[index])
    print(arrContacts)
    index = 0
    arrContacts.removeLast(contacts[index])
    print(arrContacts)

Полученные результаты:

   ["A", "B", "D", "C", "B", "D"]
   ["A", "B", "C", "B", "D"]
   ["A", "B", "C", "D"]

Важно: массив, из которого вы удаляете элементы, должен содержать элементы Equatable (такие как объекты, строки, числа и т. Д.)

Эндрю Королевство
источник