21

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.

Chen Li Yong
  • 5,459
  • 8
  • 58
  • 124

3 Answers3

24

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)
        }
    }
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • 3
    If `Element` is `Equatable`, you can just use `!contains(element)` – Alexander Oct 02 '17 at 03: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!!! – Chen Li Yong Oct 02 '17 at 03:34
1

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)
    }
}
Rand
  • 165
  • 1
  • 1
  • 12
  • 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. – Chen Li Yong Mar 21 '19 at 06:29
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().

Asad Jamil
  • 198
  • 9