Usuwanie obiektu z tablicy w Swift 3

93

W mojej aplikacji dodałem jeden obiekt w tablicy po zaznaczeniu komórki i odznaczeniu i usunięciu obiektu po ponownym wybraniu komórki. Użyłem tego kodu, ale daj mi błąd.

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

Daje mi 2 takie błędy:

C-style for statement has been removed in Swift 3
Value of type '[Any]' has no member 'removeObject'
Kamlesh Shingarakhiya
źródło
Możesz użyć Set<Contact>raczej niż Array. Czy możesz podać więcej informacji na temat przedmiotu kontaktu? Jeśli zrobiłeś to sam, będziesz potrzebować go, aby dostosować się Hashablei Equatableumieścić go w zestawie
Paulw11

Odpowiedzi:

166

Swift odpowiednikiem NSMutableArray's removeObjectto:

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

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

jeśli obiekty są unikalne . W ogóle nie ma potrzeby rzucania NSArrayi używaniaindexOfObject:

Interfejs API index(of:również działa, ale powoduje to niepotrzebne niejawne rzutowanie mostu do NSArray.

Jeśli istnieje wiele wystąpień tego samego obiektu, użyj filter. Jednak w przypadkach, takich jak tablice źródeł danych, w których indeks jest powiązany z określonym obiektem, firstIndex(ofjest preferowany, ponieważ jest szybszy niż filter.

Aktualizacja:

W Swift 4.2+ możesz usunąć jedno lub wiele wystąpień za betapomocą removeAll(where:):

array.removeAll{$0 == "beta"}
vadian
źródło
34
To najlepsza odpowiedź, ale głupotą jest nie mieć usunięcia (obiekt: „beta”).
zeeple
5
Myślę, że .index(of: jest dostępny tylko wtedy, gdy kolekcja zawiera Equatabletypy.
Adam Waite
@AdamWaite Tak, ale dotyczy to również typów Fundacji.
vadian
To nie jest w porządku, a co jeśli masz więcej niż jedną wersję „beta”? Działa to tylko wtedy, gdy tablica nie zawiera więcej niż jednego wystąpienia. Prawidłowa odpowiedź to użycie filtra lub uruchomienie tej odpowiedzi za chwilę,while let index = array.index(of: "beta") { array.remove(at: index) }
juancazalla
@juancazalla Masz rację, ale w przypadku, gdy tablica może zawierać więcej niż jedno wystąpienie, użyj filterrozwiązania. Ale jeśli przedmioty są wyjątkowe, zawsze używaj ich, index(ofponieważ są znacznie bardziej wydajne niżfilter
vadian
72
var a = ["one", "two", "three", "four", "five"]

// Remove/filter item with value 'three'
a = a.filter { $0 != "three" }
nyxee
źródło
7
To jest poprawne rozwiązanie Swift, które korzysta z dobrodziejstw składni oferowanych przez język.
Michael,
1
A jeśli przedmiot jest przedmiotem?
TomSawyer
@TomSawyer, aby odfiltrować obiekt, użyj $ 0! ==
Mike Taverne
25

W przypadku Swift 3 możesz użyć indeksu (gdzie :) i dołączyć zamknięcie, które porównuje obiekt w tablicy ($ 0) z tym, czego szukasz.

var array = ["alpha", "beta", "gamma"]
if let index = array.index(where: {$0 == "beta"}) {
  array.remove(at: index)
}
Mark Semsel
źródło
czy to zadziała, jeśli chcę usunąć wiele obiektów? jak (gdzie: {$ 0 == "beta" || $ 0 == "gamma"})
Irshad Mohamed
16

Kolejnym fajnym i przydatnym rozwiązaniem jest stworzenie takiego rozszerzenia:

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
    }

}

W ten sposób, jeśli masz tablicę z niestandardowymi obiektami:

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) 
Luca Davanzo
źródło
1
Działa to tylko wtedy, gdy tablica nie zawiera więcej niż jednego wystąpienia. Prawidłowa odpowiedź to użycie filtra lub uruchomienie tej odpowiedzi za jakiś czas. Jako użytkownik tego rozszerzenia oczekuję, że usunie wszystkie wystąpienia, a nie tylko jedno
juancazalla
To fajne, ale powinno być, remove(element: Element)ponieważ w Array możesz przechowywać również typy takie jak Int, Double - nie są to obiekty.
Radek Wilczak
8

W Swift 5 użyj tego Extension:

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

przykład:

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

W Swift 3 użyj tego Extension:

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

przykład:

var array = ["alpha", "beta", "gamma"]
array.remove(element: "beta")
Mohsenasm
źródło
6
  1. for var index = self.indexOfObject(object); index != NSNotFound; index = self.indexOfObject(object) jest dla pętli w stylu C i został usunięty

  2. Zmień kod na coś takiego, aby usunąć wszystkie podobne obiekty, jeśli zostały zapętlone:

    let indexes = arrContacts.enumerated().filter { $0.element == contacts[indexPath.row] }.map{ $0.offset }
    for index in indexes.reversed() {
       arrContacts.remove(at: index)
    }
    
Tj3n
źródło
enumerated -> filter -> map and remove (at) to inteligentne rozwiązanie. Polecam ten
Ryan X
4

Szybki 4

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

if let index = students.firstIndex(where: { $0.hasPrefix("A") }) {
   students.remove(at: index)
}
Sergey Di
źródło
3

Prawidłowe i działające jednowierszowe rozwiązanie do usuwania unikalnego obiektu (o nazwie „objectToRemove”) z tablicy tych obiektów (o nazwie „tablica”) w języku Swift 3 to:

if let index = array.enumerated().filter( { $0.element === objectToRemove }).map({ $0.offset }).first {
   array.remove(at: index)
}
Joerg
źródło
1

Spróbuj tego w Swift 3

array.remove(at: Index)

Zamiast

array.removeAtIndex(index)

Aktualizacja

"Declaration is only valid at file scope".

Upewnij się, że obiekt jest w zasięgu. Możesz określić zakres jako „wewnętrzny”, co jest wartością domyślną.

index(of:<Object>) do pracy, klasa powinna się dostosować Equatable

Dili
źródło
1

W Swift 3 i 4

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

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

Od wersji Swift 4.2 możesz zastosować bardziej zaawansowane podejście (szybsze i wydajniejsze pod względem pamięci)

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

zamiast

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

Przeczytaj więcej tutaj

yoAlex5
źródło
1

Rozszerzenie dla array, aby zrobić to łatwo i umożliwić łączenie w łańcuch dla Swift 4.2 i nowszych:

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
    }
}
Renetik
źródło
Etykiety argumentów „(gdzie :)” nie pasują do żadnych dostępnych przeciążeń
jeet.chanchawat
1
@ jeet.chanchawat no chyba w innej wersji swift ... Och to pytanie dotyczyło 3? Cóż, myślę, że miałem 4.2 w momencie pisania, ale nie pamiętam teraz, sprawdzę później, na pewno zadziałało mi
Renetik
0

To jest oficjalna odpowiedź na znalezienie indeksu konkretnego obiektu, wtedy możesz łatwo usunąć dowolny obiekt używając tego indeksu:

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

Oto link: https://developer.apple.com/documentation/swift/array/2994720-firstindex

Mahesh Cheliya
źródło
0

To jest to, czego użyłem (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)

Wyniki:

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

Ważne: tablica, z której usuwasz elementy, musi zawierać elementy Equatable (takie jak obiekty, ciągi znaków, liczba itp.)

Andrew Kingdom
źródło