1

If I got this array:

[0, 0, 1, 1, 1, 2, 3, 4, 5, 5]

How can I get:

[2, 3, 4]

This answer will hold the duplicate's value, but I want to remove that value as well.

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
NoKey
  • 129
  • 11
  • 32

2 Answers2

5

Note that you need to import Foundation to use NSCountedSet. If you need a pure Swift solution to find the unique elements of a collection you can extend it constraining the elements to the Equatable protocol and check if you can not find the element index again checking if index(of: element) == nil using the index(after:) the element index as the startIndex of the collection subsequence:

Edit/update: Swift 4.2.1

Note that extending RangeReplacebleCollection it will cover StringProtocol types Stringand Substring as well:

Constraining the element to Equatable

Xcode 11 • Swift 5.1

extension RangeReplaceableCollection where Element: Equatable {
    var uniqueElements: Self {
        filter { self[index(after: firstIndex(of: $0)!)...].firstIndex(of: $0) == nil }
    }
    mutating func removeAllNonUniqueElements() { self = uniqueElements }
}

If you don't need to keep the collection original order, you can take advantage of the new Dictionary initializer but this would need constrain the elements to Hashable:

init<S>(grouping values: S, by keyForValue: (S.Element) throws -> Dictionary<Key, Value>.Key) rethrows where Value == [S.Element], S : Sequence

and keep the keys where the values equals to 1

extension RangeReplaceableCollection where Element: Hashable {
    var uniqueElementsSet: Set<Element> {
       Set(Dictionary(grouping: self, by: { $0 }).compactMap{ $1.count == 1 ? $0 : nil })
    }
}

Playground Testing

let numbers = [0, 0, 1, 1, 1, 2, 3, 4, 5, 5]
numbers.uniqueElements    // [4, 2, 3]

If you need to keep the collection's order you can get the element frequency as shown in this answer and filter the unique elements:

extension Sequence where Element: Hashable {
    var frequency: [Element: Int] { reduce(into: [:]) { $0[$1, default: 0] += 1 } }
}

extension RangeReplaceableCollection where Element: Hashable {
    var uniqueElements:  Self { filter { frequency[$0] == 1 } }
    mutating func removeAllNonUniqueElements() { self = uniqueElements }
}

Playground testing

let numbers = [0, 0, 1, 1, 1, 2, 3, 4, 5, 5]
numbers.uniqueElements   // [2, 3, 4]

var string = "0011123455"
string.uniqueElements       // "234"
string.uniqueElementsSet    // {"4", "3", "2"}
print(string)  // "0011123455\n"

 // mutating the string
string.removeAllNonUniqueElements()
print(string)  // 234
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
3

If you want to filter only the numbers that appear 1 single time you can use the good old NSCountedSet

let nums = [0, 0, 1, 1, 1, 2, 3, 4, 5, 5]
let countedSet = NSCountedSet(array: nums)
let uniques = nums.filter { countedSet.count(for: $0) == 1 }

print(uniques) // [2, 3, 4]
Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148