22

Because I use this routine a lot, can somebody create an extension method of Swift array which will detect whether if the data that is going to be appended already exists, then it's not appended? I know that it's only a matter of few code like this:

var arr = [Int]()
for element in inputArr {
    if !arr.contains(element) { arr.append(element); }
}

Becomes:

var arr = [Int]()
for element in inputArr { arr.appendUnique(element); }

Or:

var arr = [String]()
for element in inputArr {
    if !arr.contains(element) { arr.append(element); }
}

Becomes:

var arr = [String]()
for element in inputArr { arr.appendUnique(element); }

Same method for different element types. Frankly, from this simple code, I also want to learn on how to extend the Collection with variable types. It fascinates me how Array's methods can have different parameter types whenever the object was initialized with different parameter types. Array and Dictionary are two things that I still don't get how to extend them properly. Thanks.

2
  • 1
    why not use Set instead?
    – koropok
    Commented Oct 2, 2017 at 3:16
  • 3
    A set has no order
    – Leo Dabus
    Commented Oct 2, 2017 at 3:17

4 Answers 4

25

You can extend RangeReplaceableCollection, constrain its elements to Equatable and declare your method as mutating. If you want to return Bool in case the appends succeeds you can also make the result discardable. Your extension should look like this:

extension RangeReplaceableCollection where Element: Equatable {
    @discardableResult
    mutating func appendIfNotContains(_ element: Element) -> (appended: Bool, memberAfterAppend: Element) {
        if let index = firstIndex(of: element) {
            return (false, self[index])
        } else {
            append(element)
            return (true, element)
        }
    }
}
2
  • 3
    If Element is Equatable, you can just use !contains(element)
    – Alexander
    Commented Oct 2, 2017 at 3:30
  • 4
    Oh wow, @discardableResult, Equatable, Element, I'm learning so much things in just one post! I've been thinking about how to make a result discardable so I don't need to use _ = ... but didn't know that such thing exists so I never look it up. Thanks!!! Commented Oct 2, 2017 at 3:34
2

I needed to prepend a unique element (removing it first if it already exists).

extension RangeReplaceableCollection where Element: Equatable
{
    mutating func prependUnique(_ element: Element) {
        if let index = firstIndex(of: element) {
            remove(at: index)
        }
        insert(element, at: startIndex)
    }
}
2
  • 1
    The difference between this approach and the accepted answer approach is that the accepted answer approach preserves the existing object index, while this approach uses the new object index instead. But my question is specifically about appending data. In my case, it's better if we preserve the existing object index instead. But thanks for your input. Commented Mar 21, 2019 at 6:29
  • Useful to track order of seen posts! Thanks
    – Medhi
    Commented Mar 11 at 10:21
1
    var arr = [String]()
    if !arr.contains(element) {
          arr.append(element)
    }

or 
extension Array where Element: Equatable {
    mutating func appendIfNotExists(_ element: Element) {
        if !self.contains(element) {
            self.append(element)
        }
    }
}

// Usage example
var myArray = [1, 2, 3]
myArray.appendIfNotExists(2) // Will not append since 2 already exists
myArray.appendIfNotExists(4) // Will append since 4 does not exist

print(myArray) // Output: [1, 2, 3, 4]
1
  • Thank you for your interest in contributing to the Stack Overflow community. This question already has a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation? Commented May 22 at 0:35
0

In my case I was filtering results and appending on clicking search button with api response but appending uniquely slow down the process as it has to check every index for unique, I basically made my local array empty or simply arr.removeAll().

Not the answer you're looking for? Browse other questions tagged or ask your own question.