66

Is there no easy way to remove a specific element from an array, if it is equal to a given string? The workarounds are to find the index of the element of the array you wish to remove, and then removeAtIndex, or to create a new array where you append all elements that are not equal to the given string. But is there no quicker way?

TimWhiting
  • 2,315
  • 4
  • 20
  • 38
  • 1
    Have you looked at the methods in the NSMutableArray class reference, like removeObject:, or removeObjectIdenticalTo:? – rdelmar Jan 10 '15 at 17:12
  • I am using swift, and the way I find out what I can do with an array, is to put a dot after its name and see what options xcode suggests, these include removeAll, removeAtIndex, removeLast, and removeRange. I can't see any reference to removeObjectIdenticalTo. – TimWhiting Jan 10 '15 at 19:32
  • 1
    Your question didn't specify whether you were referring to a Swift Array or an NSMutableArray; you can use ether in Swift. If you're programming in Swift, you won't see the NSMutableArray suggestions unless you cast your array to an NSMutableArray – rdelmar Jan 10 '15 at 20:23
  • 1
    Thanks, I only started programming a month ago so that is very helpful – TimWhiting Jan 11 '15 at 00:00

8 Answers8

155

You can use filter() to filter your array as follow

var strings = ["Hello","Playground","World"]

strings = strings.filter { $0 != "Hello" }

print(strings)   // "["Playground", "World"]\n"

edit/update:

Xcode 10 • Swift 4.2 or later

You can use the new RangeReplaceableCollection mutating method called removeAll(where:)

var strings = ["Hello","Playground","World"]

strings.removeAll { $0 == "Hello" }

print(strings)   // "["Playground", "World"]\n"

If you need to remove only the first occurrence of an element we ca implement a custom remove method on RangeReplaceableCollection constraining the elements to Equatable:

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

Or using a predicate for non Equatable elements:

extension RangeReplaceableCollection {
    @discardableResult
    mutating func removeFirst(where predicate: @escaping (Element) throws -> Bool) rethrows -> Element? {
        guard let index = try firstIndex(where: predicate) else { return nil }
        return remove(at: index)
    }
}

var strings = ["Hello","Playground","World"]
strings.removeFirst("Hello")
print(strings)   // "["Playground", "World"]\n"
strings.removeFirst { $0 == "Playground" }
print(strings)   // "["World"]\n"
Leo Dabus
  • 216,610
  • 56
  • 458
  • 536
16

Using filter like suggested above is nice. But if you want to remove only one occurrence of a value or you assume there are no duplicates in the array and you want a faster algorithm, use this:

EDIT: Swift 5 Update

if let index = array.firstIndex(of: "stringToRemove") {
    array.remove(at: index)
} else {
    // not found
}

Thanks @Thomas Mary.

Swift 3 and 4

if let index = array.index(of: "stringToRemove") {
    array.remove(at: index)
} else {
    // not found
}
Béatrice Cassistat
  • 1,018
  • 12
  • 36
9

It's not clear if by quicker you mean in terms of execution time or amount of code.

In the latter case you can easily create a copy using the filter method. For example, given the following array:

let array = ["1", "2", "3", "4", "5"]

you can create a copy with all elements but "2" as:

let filteredArray = array.filter { $0 != "2" }
Antonio
  • 70,043
  • 10
  • 143
  • 164
  • 2
    Hahaha we are in perfect sync but i was 7 seconds faster. :) – Leo Dabus Jan 10 '15 at 17:15
  • 7
    @LeonardoSavioDabus: Haha, yes, but I was interrupted by my wife asking me a question while writing the answer... so I won ;-) – Antonio Jan 10 '15 at 17:17
  • 1
    Thanks, I guess this is essentially what I have been doing, by using a for loop to go through the array and append the element to another array if it is not equal to the string I wish to remove, and when I said quicker, you're right I just mean looks tidier in my code. Thanks! – TimWhiting Jan 10 '15 at 19:29
7

You'll want to use filter(). If you have a single element (called say obj) to remove, then the filter() predicate will be { $0 != obj }. If you do this repeatedly for a large array this might be a performance issue. If you can defer removing individual objects and want to remove an entire sub-array then use something like:

var stringsToRemove : [String] = ...
var strings : [String] = ...

strings.filter { !contains(stringsToRemove, $0) }

for example:

 1> ["a", "b", "c", "d"].filter { !contains(["b", "c"], $0) }
$R5: [String] = 2 values {
  [0] = "a"
  [1] = "d"
}
GoZoner
  • 63,869
  • 20
  • 92
  • 144
4

You could use filter() in combination with operator overloading to produce an easily repeatable solution:

func -= (inout left: [String], right: String){
    left = left.filter{$0 != right}    
}

var myArrayOfStrings:[String] = ["Hello","Playground","World"]

myArrayOfStrings -= "Hello"

print(myArrayOfStrings)   // "[Playground, World]"
tukbuk23
  • 71
  • 5
2

if you need to delete subArray from array then this is a perfect solution using Swift3:

var array = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]            
let subArrayToDelete = ["c", "d", "e", "ee"]
array = array.filter{ !subArrayToDelete.contains($0) }
print(array) // ["a", "b", "f", "g", "h", "i", "j"]

this is better for your performance rather than deleting one by one.

btw even faster solution is (but it will rearrange items in the final array):

array = Array(Set(array).subtracting(subArrayToDelete))
Tung Fam
  • 7,344
  • 4
  • 52
  • 61
1
var ra = ["a", "ab", "abc", "a", "ab"]

print(ra)                               // [["a", "ab", "abc", "a", "ab"]

ra.removeAll(where: { $0 == "a" })

print(ra)                               // ["ab", "abc", "ab"]
Roi Zakai
  • 214
  • 2
  • 3
1

Simple loop over Array

var array = ["Apple","Banana","Orange"]

for (index,value) in array.enumerated(){
    if value == "Banana"{
    array.remove(at: index)
}
Threadripper
  • 544
  • 8
  • 14