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.

3
  • 2
    why not use Set instead? Commented Oct 2, 2017 at 3:16
  • 3
    A set has no order Commented Oct 2, 2017 at 3:17
  • How about OrderedSet then? Commented May 9 at 14:36

4 Answers 4

26

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

2 Comments

If Element is Equatable, you can just use !contains(element)
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!!!
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 Comments

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.
Useful to track order of seen posts! Thanks
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 Comment

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?
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().

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.