67

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?

4
  • 1
    Have you looked at the methods in the NSMutableArray class reference, like removeObject:, or removeObjectIdenticalTo:? Commented Jan 10, 2015 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. Commented Jan 10, 2015 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 Commented Jan 10, 2015 at 20:23
  • 1
    Thanks, I only started programming a month ago so that is very helpful Commented Jan 11, 2015 at 0:00

9 Answers 9

160

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 can 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"
Sign up to request clarification or add additional context in comments.

Comments

19

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
}

1 Comment

Swift 5 update : if let index = array.firstIndex(of: "stringToRemove") { array.remove(at: index) }
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" }

3 Comments

Hahaha we are in perfect sync but i was 7 seconds faster. :)
@LeonardoSavioDabus: Haha, yes, but I was interrupted by my wife asking me a question while writing the answer... so I won ;-)
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!
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"
}

Comments

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

Comments

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

Comments

2
var ra = ["a", "ab", "abc", "a", "ab"]

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

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

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

1 Comment

This is exactly the same as removeAll { $0 == "a" }
0

Simple loop over Array

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

for (index,value) in array.enumerated(){
    if value == "Banana"{
    array.remove(at: index)
}

2 Comments

you can't remove an item from a non mutable collection
Thanks Buddy @LeoDabus for correcting me i have changed from let to var now you able to do it
0

One small point.

Normally you must simply use .filter{$0 != target}

If for some reason you wish to traverse the array. It's a basic of programming that you DON'T edit/delete items from an array while you're traversing it as, depending on the exact nature of the language/OS/etc it may or may not result in unpredictable behavior for the obvious reasons.

The only safe way to traverse is (in some cases) backwards but (always) using a while formulation which re-looks at the array each pass:

    while let found = urls.firstIndex(of: target) {
        urls.remove(at: found)
    }

This is a basic of working with arrays.

Again, always use .filter{$0 != target}

As a footnote, in the incredible case that it is a performance issue (literally 10s of millions of entries), you'd handle the whole thing completely differently using indices and so on, so it is totally irrelevant to this question.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.