109

In my application I added one object in array when select cell and unselect and remove object when re-select cell. I used that code but give me error.

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

It gives me 2 error like that:

C-style for statement has been removed in Swift 3
Value of type '[Any]' has no member 'removeObject'
Cœur
  • 37,241
  • 25
  • 195
  • 267
Kamlesh Shingarakhiya
  • 2,757
  • 2
  • 16
  • 34
  • You can use a `Set` rather than an Array. Can you provide more information about your contact object? If you have made it yourself you will need it to conform to `Hashable` and `Equatable` in order to put it in a set – Paulw11 Nov 29 '16 at 06:58

13 Answers13

198

The Swift equivalent to NSMutableArray's removeObject is:

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

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

if the objects are unique. There is no need at all to cast to NSArray and use indexOfObject:

The API index(of: also works but this causes an unnecessary implicit bridge cast to NSArray.

Of course you can write an extension of RangeReplaceableCollection to emulate the function. But due to value semantics you cannot name it removeObject.

extension RangeReplaceableCollection where Element : Equatable {
    @discardableResult
    mutating func remove(_ element : Element) -> Element?
    {
        if let index = firstIndex(of: element) {
            return remove(at: index)
        }
        return nil
    }
}

Like remove(at: it returns the removed item or nil if the array doesn't contain the item.


If there are multiple occurrences of the same object use filter. However in cases like data source arrays where an index is associated with a particular object firstIndex(of is preferable because it's faster than filter.

Update:

In Swift 4.2+ you can remove one or multiple occurrences of beta with removeAll(where:):

array.removeAll{$0 == "beta"}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • 44
    This is the best answer, but it is beyond stupid not to have a remove(object: "beta"). – zeeple Mar 01 '17 at 23:44
  • 5
    I think `.index(of: ` is only available if the collection contains `Equatable` types. – Adam Waite Sep 17 '17 at 14:53
  • @AdamWaite Yes but this applies also to the Foundation types. – vadian Sep 17 '17 at 14:58
  • 1
    This isn't right, what if you have more than one "beta"? This only works if the array does not contain more than one occurrences. The right answer is using a filter or running this answer in a while, `while let index = array.index(of: "beta") { array.remove(at: index) }` – juancazalla May 31 '18 at 15:13
  • @juancazalla You are right but in the case if the array can contain more than one occurrence use the `filter` solution. But if the objects are unique use always `index(of` because it's much more performant than `filter` – vadian May 31 '18 at 15:34
  • @vadian I've read that you are a musician too and I was wondering if you could help me out with an answer https://stackoverflow.com/questions/53349969 . Many thanks – bibscy Nov 17 '18 at 09:51
75
var a = ["one", "two", "three", "four", "five"]

// Remove/filter item with value 'three'
a = a.filter { $0 != "three" }
nyxee
  • 2,773
  • 26
  • 22
26

For Swift 3, you can use index(where:) and include a closure that does the comparison of an object in the array ($0) with whatever you are looking for.

var array = ["alpha", "beta", "gamma"]
if let index = array.index(where: {$0 == "beta"}) {
  array.remove(at: index)
}
Mark Semsel
  • 7,125
  • 3
  • 29
  • 27
17

Another nice and useful solution is to create this kind of extension:

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
    }

}

In this way, if you have your array with custom objects:

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
  • 21,000
  • 15
  • 120
  • 146
  • 1
    This only works if the array does not contain more than one occurrences. The right answer is using a filter or running this answer in a while. As a user of this extension, I expect it to remove all the occurrences and not only one – juancazalla Jun 01 '18 at 14:12
  • It's nice, but it should be `remove(element: Element)` because in Array you can store also types like Int, Double - they are not objects. – Radek Wilczak Oct 03 '18 at 07:49
15

In Swift 5, Use this Extension:

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

example:

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

In Swift 3, Use this Extension:

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

example:

var array = ["alpha", "beta", "gamma"]
array.remove(element: "beta")
Mohsenasm
  • 2,916
  • 1
  • 18
  • 22
6
  1. for var index = self.indexOfObject(object); index != NSNotFound; index = self.indexOfObject(object) is for loop in C-style and has been removed

  2. Change your code to something like this to remove all similar object if it have looped:

    let indexes = arrContacts.enumerated().filter { $0.element == contacts[indexPath.row] }.map{ $0.offset }
    for index in indexes.reversed() {
       arrContacts.remove(at: index)
    }
    
Cœur
  • 37,241
  • 25
  • 195
  • 267
Tj3n
  • 9,837
  • 2
  • 24
  • 35
4

The correct and working one-line solution for deleting a unique object (named "objectToRemove") from an array of these objects (named "array") in Swift 3 is:

if let index = array.enumerated().filter( { $0.element === objectToRemove }).map({ $0.offset }).first {
   array.remove(at: index)
}
Joerg
  • 41
  • 2
4

Swift 4

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

if let index = students.firstIndex(where: { $0.hasPrefix("A") }) {
   students.remove(at: index)
}
Serhii Didanov
  • 2,200
  • 1
  • 16
  • 31
4

Swift Remove object from array

In Swift 3 and Swift 4

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

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

From Swift 4.2 you can use more advanced approach(faster and memory efficient)

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

instead of

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

Read more here

yoAlex5
  • 29,217
  • 8
  • 193
  • 205
1

Try this in Swift 3

array.remove(at: Index)

Instead of

array.removeAtIndex(index)

Update

"Declaration is only valid at file scope".

Make sure the object is in scope. You can give scope "internal", which is default.

index(of:<Object>) to work, class should conform to Equatable

Dileep
  • 2,399
  • 4
  • 26
  • 39
1

Extension for array to do it easily and allow chaining for Swift 4.2 and up:

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
  • 5,887
  • 1
  • 47
  • 66
  • Argument labels '(where:)' do not match any available overloads – jeet.chanchawat May 25 '19 at 11:19
  • 1
    @jeet.chanchawat well probably different swift version then ... Oh this question was for 3 ? Well I think I had 4.2 at time of writing but don't remember now, will check it later, it definitely worked for me – Renetik May 26 '19 at 05:37
1

This is official answer to find index of specific object, then you can easily remove any object using that index :

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

Here is the link: https://developer.apple.com/documentation/swift/array/2994720-firstindex

Mahesh Cheliya
  • 1,334
  • 1
  • 16
  • 21
0

This is what I've used (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)

Results:

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

Important: The array from which you remove items must contain Equatable elements (such as objects, strings, number, etc.)

Andrew Kingdom
  • 188
  • 1
  • 9