Removendo objeto da matriz em Swift 3

95

Em meu aplicativo, adicionei um objeto na matriz ao selecionar a célula e desmarque e remova o objeto ao selecionar novamente a célula. Eu usei esse código, mas me deu o erro.

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

Isso me dá 2 erros como este:

C-style for statement has been removed in Swift 3
Value of type '[Any]' has no member 'removeObject'
Kamlesh Shingarakhiya
fonte
Você pode usar um em Set<Contact>vez de um Array. Você pode fornecer mais informações sobre seu objeto de contato? Se você mesmo o fez, precisará dele para se conformar Hashablee Equatablepara colocá-lo em um conjunto
Paulw11

Respostas:

169

O equivalente do Swift ao NSMutableArrayde removeObjecté:

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

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

se os objetos são únicos . Não há necessidade de lançar paraNSArray e usarindexOfObject:

A API index(of:também funciona, mas isso causa uma conversão de ponte implícita desnecessária paraNSArray .

Se houver várias ocorrências do mesmo objeto, use filter. No entanto, em casos como matrizes de fonte de dados em que um índice está associado a um objeto específico firstIndex(ofé preferível porque é mais rápido do quefilter .

Atualizar:

No Swift 4.2+, você pode remover uma ou várias ocorrências de betacom removeAll(where:):

array.removeAll{$0 == "beta"}
vadiana
fonte
36
Esta é a melhor resposta, mas é mais do que estúpido não ter uma remoção (objeto: "beta").
zeeple de
5
Acho que .index(of: só está disponível se a coleção contiver Equatabletipos.
Adam Waite,
@AdamWaite Sim, mas isso se aplica também aos tipos de Fundação.
vadian,
Isso não está certo, e se você tiver mais de um "beta"? Isso só funciona se o array não contém mais de uma ocorrência. A resposta certa é usar um filtro ou executar esta resposta while let index = array.index(of: "beta") { array.remove(at: index) }
daqui a
@juancazalla Você tem razão, mas no caso de o array conter mais de uma ocorrência, use a filtersolução. Mas se os objetos forem exclusivos, use sempre index(ofporque têm muito mais desempenho do quefilter
vadian
72
var a = ["one", "two", "three", "four", "five"]

// Remove/filter item with value 'three'
a = a.filter { $0 != "three" }
Nyxee
fonte
7
Essa é a solução correta do Swift, que faz uso dos goodies de sintaxe oferecidos pela linguagem.
Michael
1
E se o item for um objeto?
TomSawyer,
@TomSawyer para filtrar um objeto, use $ 0! ==
Mike Taverne
25

Para Swift 3, você pode usar o índice (onde :) e incluir um fechamento que faz a comparação de um objeto no array ($ 0) com o que você está procurando.

var array = ["alpha", "beta", "gamma"]
if let index = array.index(where: {$0 == "beta"}) {
  array.remove(at: index)
}
Mark Semsel
fonte
vai funcionar se eu quiser remover vários objetos? like (onde: {$ 0 == "beta" || $ 0 == "gamma"})
Irshad Mohamed
16

Outra solução boa e útil é criar este tipo de extensão:

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
    }

}

Desta forma, se você tiver sua matriz com objetos personalizados:

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
fonte
1
Isso só funciona se o array não contém mais de uma ocorrência. A resposta certa é usar um filtro ou ver esta resposta daqui a pouco. Como um usuário desta extensão, espero que ela remova todas as ocorrências e não apenas uma
juancazalla 01 de
É bom, mas deveria ser remove(element: Element)porque em Array você também pode armazenar tipos como Int, Double - eles não são objetos.
Radek Wilczak,
8

No Swift 5 , use isto Extension:

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

exemplo:

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

No Swift 3 , use isto Extension:

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

exemplo:

var array = ["alpha", "beta", "gamma"]
array.remove(element: "beta")
Mohsenasm
fonte
6
  1. for var index = self.indexOfObject(object); index != NSNotFound; index = self.indexOfObject(object) é for loop no estilo C e foi removido

  2. Altere seu código para algo assim para remover todos os objetos semelhantes se ele tiver um loop:

    let indexes = arrContacts.enumerated().filter { $0.element == contacts[indexPath.row] }.map{ $0.offset }
    for index in indexes.reversed() {
       arrContacts.remove(at: index)
    }
    
Tj3n
fonte
enumerado -> filtro -> mapear e remover (at) é uma solução inteligente. Recomendar este
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)
}
Sergey Di
fonte
3

A solução de uma linha correta e funcional para excluir um objeto único (denominado "objectToRemove") de uma matriz desses objetos (denominada "matriz") no Swift 3 é:

if let index = array.enumerated().filter( { $0.element === objectToRemove }).map({ $0.offset }).first {
   array.remove(at: index)
}
Joerg
fonte
1

Experimente isso no Swift 3

array.remove(at: Index)

Ao invés de

array.removeAtIndex(index)

Atualizar

"Declaration is only valid at file scope".

Certifique-se de que o objeto esteja no escopo. Você pode dar o escopo "interno", que é o padrão.

index(of:<Object>) para trabalhar, a classe deve estar em conformidade com Equatable

Dili
fonte
1

Em Swift 3 e 4

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

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

A partir do Swift 4.2, você pode usar uma abordagem mais avançada (mais rápida e com memória eficiente)

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

ao invés de

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

Leia mais aqui

yoAlex5
fonte
1

Extensão para array para fazer isso facilmente e permitir o encadeamento para Swift 4.2 e superior:

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
fonte
Rótulos de argumento '(onde :)' não correspondem a nenhuma sobrecarga disponível
jeet.chanchawat
1
@ jeet.chanchawat bem, provavelmente uma versão rápida diferente então ... Oh, essa pergunta era para 3? Bem, eu acho que tinha 4.2 no momento em que escrevi, mas não me lembro agora, vou verificar mais tarde, definitivamente funcionou para mim
Renetik
0

Esta é a resposta oficial para encontrar o índice de um objeto específico, então você pode remover facilmente qualquer objeto usando esse índice:

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

Aqui está o link: https://developer.apple.com/documentation/swift/array/2994720-firstindex

Mahesh Cheliya
fonte
0

Isso é o que eu usei (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)

Resultados:

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

Importante: a matriz da qual você remove itens deve conter elementos Equatable (como objetos, strings, número, etc.)

Andrew Kingdom
fonte