How to create an array of unique objects in Swift

How to create a unique list of objects in Swift, for example, NSSet and NSMutableSet in Objective-C.

+54
swift nsset
Jun 04 '14 at 17:48
source share
11 answers

With Swift 1.2 (Xcode 6.3 beta), Swift has its own set type. From the release notes:

A new Set data structure is included, which provides a general collection of unique elements, with full semantics of values. These are bridges with NSSet , providing functionality similar to Array and Dictionary .

Here are some simple usage examples:

 // Create set from array literal: var set = Set([1, 2, 3, 2, 1]) // Add single elements: set.insert(4) set.insert(3) // Add multiple elements: set.unionInPlace([ 4, 5, 6 ]) // Swift 3: set.formUnion([ 4, 5, 6 ]) // Remove single element: set.remove(2) // Remove multiple elements: set.subtractInPlace([ 6, 7 ]) // Swift 3: set.subtract([ 6, 7 ]) print(set) // [5, 3, 1, 4] // Test membership: if set.contains(5) { print("yes") } 

but there are much more methods available.

Update: Collections are now also documented in the "Collection Types" in the Swift documentation.

+50
Feb 10 '15 at 8:04
source share

You can use any Objective-C class in Swift:

 var set = NSMutableSet() set.addObject(foo) 
+14
Jun 04 '14 at 17:49
source share

Swift has no concept of sets. Using NSMutableSet in Swift can be slower than using Dictionary , which contains dummy values. You can do it:

 var mySet: Dictionary<String, Boolean> = [:] mySet["something"]= 1 

Then just iterate over the keys.

+10
Jun 04 '14 at 5:55
source share

I built an extensive Set type, similar to the built-in Array and Dictionary , these are one and two blog posts and the GitHub repository:

+9
Oct 10 '14 at 1:43
source share
 extension Array where Element: Hashable { var setValue: Set<Element> { return Set<Element>(self) } } let numbers = [1,2,3,4,5,6,7,8,9,0,0,9,8,7] let uniqueNumbers = numbers.setValue // {0, 2, 4, 9, 5, 6, 7, 3, 1, 8} let names = ["John","Mary","Steve","Mary"] let uniqueNames = names.setValue // {"John", "Mary", "Steve"} 
+7
Oct 19 '15 at 5:09
source share

I thought that a structure with an internal Dictionary would be a way. I just started using it, so it is not complete, and I do not know about performance yet.

 struct Set<T : Hashable> { var _items : Dictionary<T, Bool> = [:] mutating func add(newItem : T) { _items[newItem] = true } mutating func remove(newItem : T) { _items[newItem] = nil } func contains(item: T) -> Bool { if _items.indexForKey(item) != nil { return true } else { return false } } var items : [T] { get { return [T](_items.keys) } } var count : Int { get { return _items.count } } } 
+4
Jul 12 '14 at 1:12
source share

In fact, you can create a Set object quite easily (unlike GoZoner, there is a built-in method contains):

 class Set<T : Equatable> { var items : T[] = [] func add(item : T) { if !contains(items, {$0 == item}) { items += item } } } 

and you might even want to declare a user statement:

 @assignment @infix func += <T : Equatable> (inout set : Set<T>, items : T[]) -> Set<T> { for item in items { set.add(item) } return set } 
+3
Jun 25 '14 at 1:47
source share

Always in this case, the critical factor is how to compare objects and what types of objects are included in the set. Using the Swift dictionary, where Set objects are dictionary keys, can be a problem based on key type restrictions (String, Int, Double, Bool, novalue enumeration or hashing).

If you can define a hash function for your object type, you can use a dictionary. If objects are ordered , you can define a tree. If the objects are only comparable with == , you will need to iterate over the given elements in order to detect the pre-existing object.

 // When T is only Equatable class Set<T: Equatable> { var items = Array<T>() func hasItem (that: T) { // No builtin Array method of hasItem... // because comparison is undefined in builtin Array for this: T in items { if (this == that) { return true } } return false } func insert (that: T) { if (!hasItem (that)) items.append (that) } } 

The above example builds a Swift Set ; the example uses objects that are only Equatable , which, although the general case, does not necessarily lead to effective implementations of Set (search complexity (O (N) - example above).

+2
Jun 04 '14 at 18:17
source share

So, I think creating a set with an array is a terrible idea - O (n) is the temporary complexity of this set.

I put together a nice set that uses a dictionary: https://github.com/evilpenguin/Swift-Stuff/blob/master/Set.swift

+1
Jun 25 '14 at 19:49
source share

I wrote a function to solve this problem.

 public func removeDuplicates<C: ExtensibleCollectionType where C.Generator.Element : Equatable>(aCollection: C) -> C { var container = C() for element in aCollection { if !contains(container, element) { container.append(element) } } return container } 

To use it, just pass an array that contains duplicate elements for this function. And then it will return an array guaranteed by uniqueness.

You can also pass a Dictionary , String or something that matches the ExtensibleCollectionType protocol if you want.

+1
Jan 05 '15 at 20:47
source share

Special case for classes derived from NSObject

given that Equitable is garbage by default, you'd better make sure you provide the correct one

 static func == (lhs: YourClassDerivedFromNSObject, rhs: YourClassDerivedFromNSObject) -> Bool { 

implementation to make you want to pluck duplicates inserted in Set

0
Jul 02 '19 at 7:22
source share



All Articles