Coding Explorer Blog

Exploring how to code for iOS in Swift and Objective-C

  • Home
  • Apps
  • About
  • Contact

Swift Set Type

Xcode 11.6 Swift 5.2.4

Last updated on August 12, 2020

In Objective-C there were three basic types of Data Structures, NSArray, NSDictionary, and NSSet.  In Objective-C, the immutable and mutable forms were separate, so you also had NSMutableArray, NSMutableDictionary, and NSMutableSet.  Especially since it is often an interview question, I should point out that a Data Structure is simply “a particular way of organizing data in a computer so that it can be used efficiently.”  You can read that sentence and more at the Wikipedia article about Data Structures.

In Swift we were greeted with Arrays and Dictionaries, but there was no Set to be found!  You had to go back to Objective-C’s NSSet to work with them.  You can read about the other two Data Structures back in the previous posts Arrays and their Methods in Swift and Swift Dictionary Quick Reference.  Well, thanks to Swift 1.2, Set has come to the land of Swift.

What is a Set?

What is a Set you might ask?  A set is an unordered collection of distinct objects.

How is that different from an array?  Well, an array is a ordered collection, firstly.  Each member of an array is at a specific index that you access through it’s subscript (the [#] next to the array, where you replace the # sign with the index you’re looking for).  Secondly, the objects at each index need not be distinct.  You can have the same object in at all of hte indexes if you really want to for some reason.

How is it different from a dictionary?  A dictionary is an unordered collection of values referenced by specific keys.  If you have a dictionary of people that are keyed by a String, you would call one with the subscript syntax as well, like ‘ Person[“Johnny”] ‘.  So while it is unordered, like the Set is, each value in it is addressed by a specific key.

So if it isn’t indexed, or even addressable by a key, what would you possibly use a set for?

Does order matter with every collection of Objects?  Let’s say you were having a potluck dinner, and each person told the host/hostess what they were bringing.  You want people to see what foods are available, and the quantity doesn’t matter.

Some people have responded that they’ll bring “Salad”, “Chips”, and “Sandwiches”.  Somebody else comes along and says they also want to bring a “Salad”.  Salad’s good for you right?  Everybody should eat Salad more often!

So now, you have had people respond that there will be “Salad”, “Chips”, “Sandwiches”, and “Salad”.  If somebody is asking what will be available to eat at this dinner, do you really need to say “Salad” twice?  And does order matter at all?  Not really.  So a Set of what is available at this dinner is “Salad”, “Chips”, and “Sandwiches” still, even if there is more salad.

But an array is easy, and to make sure we don’t put in the same thing more than once we could just iterate through the array, see if that Object is there, and just not enter it right?  Well, checking for if that object is there usually means iterating over the rest of the array before finding the index that object is in, if it is in there at all.  That is part of where the Set shines.  The Set is a lot faster than an array.  The Set is most similar to the keys of a Dictionary type.  The keys of a dictionary must be unique, and you find that key-value pair (under the hood) by generating a hash of the key, and check for something at that hash.  That is effectively what the Set type does.

If you try to insert something that is already in the Set, it should just accept it, but not actually do anything.  So if we added “Salad” to our food set after the first three were added, we would still result in a set of 3 objects.  So you don’t need to check if the object is there (yourself) before inserting it.  You can just try anyway.

However, if you DID want to check if something is in there, say to limit people from bringing the same thing to this dinner, Set is particularly fast at testing whether an object is in the Set itself.  Since it doesn’t have to iterate over itself like the Array, it just checks whether there is something at that value’s hash.  If there is, it exists, if there is not, then it doesn’t.  This is similar to a Dictionary, where it will return nil if you try to get something from the Dictionary with a key that is not present in that Dictionary.

Anyway, enough chit chat, how do you use Sets in Swift?

Swift Sets and their Methods

These are most of the methods and properties available for the Set type:

Title Example Description
Initialize Set Set<String>() This initializes a completely empty Set.
Initialize Set from Array Literal let abcSet = Set([“A”, “B”, “C”, “D”]) This creates a Set that contains the Strings of the first 4 letters of the alphabet.
Check Set Membership foodSet.contains(“Sandwiches”) This returns a Bool of whether foodSet already contains the Swift String “Sandwiches”.
Insert Value foodSet.insert(“Salad”) This will insert the Swift String “Salad” into the Set.
Remove Value foodSet.remove(“Chips”) This will remove a value from the array.  This will return an Optional of the Set’s type, with the value if it was there, or nil if it wasn’t.
Remove All Values foodSet.removeAll() This will completely empty the Set of all members.
Number of Elements foodSet.count Returns an Int of how many elements are in the Set.
Check if Empty foodSet.isEmpty Returns a Bool of true if the foodSet has no members.  It returns false if there are any members in the Set.
Check if Subset entreeSet.isSubset(of: foodSet) Returns true if entree set is a subset of foodSet.  That is, if all of entreeSet’s members are in foodSet.
Check if Strict Subset entreeSet.isStrictSubset(of: foodSet) Returns true if  entree set is a subset of foodSet, but not an exact copy.
Check if Superset foodSet.isSuperset(of: entreeSet) Returns true if foodSet is a superset of entreeSet.  That is, if foodSet contains all members of entreeSet.
Check if Strict Superset sameFoodSet.isStrictSuperset(of: foodSet) Returns true only if sameFoodSet is a superset of foodSet, but not an exact copy.
Check if no members same foodSet.isDisjoint(with otherFoods) Returns true if the two Sequences have no members in common.
Combine Sets foodSet.union(otherFoods) Returns a new Set containing the members of both foodSet and otherFoods.
Combine Sets In Place otherFoods.formUnion(foodSet) Mutates the otherFoods Set to add the members of foodSet to it.
Subtract One Set From Another otherFoods.subtracting(entreeSet) Returns a new Set with the values of entreeSet removed from otherFoods, if they were present.
Subtract One Set From Another In Place otherFoods.subtract(entreeSet) Mutates the otherFoods Set to subtract an values that were specified in the entreeSet (like above).
Create Set of Common Members moreFoods.intersection(entreeSet) Returns a new Set with the values that were in common between moreFoods and entreeSet.
Create Set of Common Members In Place moreFoods.formIntersection(entreeSet) Mutates the moreFoods Set to perform the intersect method above.
Create Set of Uncommon Members moreFoods.symmetricDifference(dessertsSet) Returns a new Set containing the values that were in either moreFoods or dessertsSet, but NOT both.
Create Set of Uncommon Members In Place moreFoods.formSymmetricDifference(dessertsSet) Mutates the moreFoods Set with the result of the exclusiveOr method above.

If you look inside the Swift Set type in Swift 1.2 (and Swift 2.1 now), you will see that it actually does refer to Indexes in there.  It has to be addressable by the system somehow, but you probably should not mess with them.  I mean you technically can, but since the thing is unordered, the actual order they appear in will be… unpredictable is probably the wrong word, but not something I would rely on.

One thing to note, all of the methods from isSubsetOf down actually take a SequenceType value.  That is, a type that adopts the SequenceType protocol.  So actually, these should take even an Array, since Arrays m adopt the SequenceType protocol.  When making a union with an array, it adds the values stored at each index to the Swift Set.  A Dictionary technically is a SequenceType as well, but I think the type fails the Generic’s where clause, since it is composed of the key and value types.  However, if you can create a union between a set and either the keys or values of the array using the appropriate property of that dictionary for either the keys or values.

Initializing a Swift Set

As shown above, there are a few ways to initialize a Swift Set.  There isn’t technically a “Set Literal”, at least not that I’ve seen yet, but  you can create a Swift Set with an Array Literal.  Each of these ways to create a Swift Set are valid:

var someSet = Set<String>()
let abcSet: Set = ["A", "B", "C", "D"]
var foodSet = Set(["Salad", "Chips", "Sandwiches"])

The first one makes a completely empty mutable Swift Set.  The next makes a constant Set by explicitly typing that it will be a Set and then setting it with an array literal.  The final one makes a mutable Set using an initializer that takes a Swift Array Literal.  Notice how the last two don’t necessarily need the type of the elements of the Set stated explicitly.  You can if you wish, but doing this worked correctly.  I don’t know which way is best practice yet, but these are the ones currently available.  Maybe we’ll get a true Set literal in a future beta, but for now we can do it well enough with arrays.

Adding and Removing Elements from a Swift Set

The simplest way to add or remove items from a Set is with the insert or remove commands:

foodSet.remove("Chips")  //Now contains: {"Salad", "Sandwiches"}

foodSet.insert("Soup")  //Now contains: {"Salad", "Sandwiches", "Soup"}

foodSet.removeAll()  //Now Contains 0 members

As mentioned in the table, the remove method actually returns an Optional of the type stored in the Set.  It will return nil if the value you want to remove isn’t there in the first place, or it will return the value itself.  If you wanted to just remove everything of course, you can just run the method removeAll().

Checking for Element Membership in Sets

The count and isEmpty properties are pretty self explanatory, so we won’t really go over them in any more depth.  The rest of the methods however, are rather interesting.  The first inquires if a specific element is in the Swift Set, with a Bool return value.  The rest in this batch return Bools depending on what the relationship is between the Swift Set and another Sequence type (which is usually a Set, but as mentioned earlier, they could work with Arrays).

//foodSet     is {"Salad", "Chips", "Sandwiches"}
//entreeSet   is {"Salad", "Sandwiches"}
//sameFoodSet is {"Salad", "Chips", "Sandwiches"}
//otherFoods  is {"Quiche", "Donuts"}

foodSet.contains("Chips")               //returns true

entreeSet.isSubset(of: foodSet)           //returns true
sameFoodSet.isStrictSubset(of: foodSet)   //returns false

foodSet.isSuperset(of: entreeSet)         //returns true
foodSet.isStrictSuperset(of: sameFoodSet) //returns false
foodSet.isStrictSuperset(of: entreeSet)   //returns true

foodSet.isDisjoint(with: entreeSet)       //returns false
foodSet.isDisjoint(with: otherFoods)      //returns true

The contains method returns true, since “Chips” is indeed a member of the foodSet.

Since entreeSet contains “Salad” and “Sandwiches”, it is a subset of the entire foodSet, so that returns true.  However, while sameFoodSet is a subset of foodSet by containing all of the same items, it is not a Strict subset, which basically means it must be PART of the set, not the whole thing, so it returns false.

The foodSet is a superset of the of the entreeSet, by containing at least everything in the entreeSet.  Like before, foodSet is not a Strict superset of sameFoodSet, since they are entirely the same.  However, foodSet does contain everything in entreeSet, and something that entree set does not (the “Chips), so the isStrictSuperSetOf returns true for that inquiry.

Finally, we can check if they are disjoint, basically saying if they have nothing in common.  The foodSet and entreeSet contain a few members in common, so isDisjointWith evaluates to false.  However, there is nothing in common between foodSet and otherFoods, so that one evaluates to true.

Compare Sets

The final batch create new Sets based on how the Swift Set and SequenceTypes being compared actually compare.

//foodSet     is {"Salad", "Chips", "Sandwiches"}
//otherFoods  is {"Quiche", "Donuts"}
//entreeSet   is {"Salad", "Sandwiches"}
//dessertsSet is {"Chips", "Ice Cream", "Donuts"}

let moreFoods = foodSet.union(otherFoods)
//moreFoods now contains {"Sandwiches", "Salad", "Chips", "Quiche", "Donuts"}


otherFoods.formUnion(foodSet)
//otherFoods now contains {"Sandwiches", "Quiche", "Salad", "Chips", "Donuts"}

otherFoods.subtract(entreeSet)
//otherFoods Set containing {"Quiche", "Chips", "Donuts"}

otherFoods.intersection(dessertsSet)
//Returns Set containing {"Chips", "Donuts"}

foodSet.symmetricDifference(dessertsSet)
//Returns Set containing {"Sandwiches", "Ice Cream", "Salad", "Donuts"}

Each of them comes with a form<noun> style, which replaces the Swift Set it is called from with the result of whichever <noun> method is called.  Of course the Set this is called from must be a variable to use those versions.  We didn’t go over each form<noun> style of method, but they all work the same way.  You call them like shown above with formUnion.

Union does what it sounds like.  It combines the two sets.  If there were any in common, they would only be shown once in the resulting Swift Set.  So basically, we had “Salad”, “Chips”, “Sandwiches” from foodSet, and combined those with “Quiche” and “Donuts” from otherFoods, to get back a new set containing all of them: {“Donuts”, “Sandwiches”, “Salad”, “Quiche”, “Chips”}.

Next, we outright replaced otherFoods with the union of itself with foodSet.  After that, we requested a new Set with the result of subtracting the members of entreeSet (“Salad”, “Sandwiches”) from the newly modified otherFoods (“Chips”, “Sandwiches”, “Quiche”, “Salad”, “Donuts”) to result in a returned Set of (“Soup”, “Quiche”, “Donuts”).

Then we requested a set that was the intersection of otherFoods (“Chips”, “Sandwiches”, “Quiche”, “Salad”, “Donuts”) and dessertsSet (“Chips”, “Ice Cream”, “Donuts”). The only things in common between those two were “Chips” and “Donuts”, so those were the members of the returned Set.

SymmetricDifference then, creates a new Swift Set containing the members that are in either Set, but NOT both. So when taking the symmetricDifference of foodSet (“Salad”, “Chips”, “Sandwiches”) and dessertsSet (“Chips”, “Ice Cream”, “Donuts”), they have “Chips” in common, so that element is left out. This then results in a Set that is the combination of the remaining members (“Sandwiches”, “Ice Cream”, “Salad”, “Donuts”). This is basically an Exclusive Or operation.

Conclusion

A Swift Set provides fast lookup for whether a value is contained therein, especially compared with an Array.  When you need to know that an object is in a collection, need it to be unique, but don’t care about the order, the Swift Set may be just the Data Structure for you.  I have not used their Objective-C equivalent that much yet, but after learning all of this about the Swift version, I will probably be using them quite a bit more often.

This wasn’t explicitly necessary in the main part of the post, but I thought I should point out a bit more about Data Structures.  It sounds like a very highfalutin (huh, never actually written that word) word that you’ll need a degree in Computer Science to understand, but it really just means that, Structures to store Data in.  So that means it includes the simple container types in many programming languages like Arrays, Dictionaries, and Sets.  It also includes Queues, Stacks, Trees (like a Family Tree), and many more.  You can read a lot about a lot more of them at Wikipedia’s List of Data Structures page.

I hope you found this article helpful.  If you did, please don’t hesitate to share this post on Twitter or your social media of choice, every share helps.  Of course, if you have any questions, don’t hesitate to contact me on the Contact Page, or on Twitter @CodingExplorer, and I’ll see what I can do.  Thanks!

Sources

  • The Swift Programming Language – Apple Inc.
  • Data Structures — Wikipedia
  • NSSet Class Reference — Apple Inc.
  • NSSet — RyPress
  • Collection Data Structures In Swift — Ray Wenderlich

Filed Under: Swift Tagged With: Swift

Subscribe to the Coding Explorer Newsletter

* indicates required

Follow Us

Facebooktwitterrss

Recent Posts

  • Error Handling in Swift
  • Creating and Modifying a URL in your Swift App
  • Watch Connectivity in Swift — Application Context
  • watchOS Hello World App in Swift
  • API Availability Checking in Swift

Categories

  • Class Reference
  • General
  • Interface Builder
  • My Apps
  • Objective-C
  • Swift
  • Syntax
  • Tutorial
  • Uncategorized

Archives

  • May 2016
  • March 2016
  • February 2016
  • December 2015
  • July 2015
  • June 2015
  • April 2015
  • February 2015
  • January 2015
  • December 2014
  • November 2014
  • October 2014
  • September 2014
  • August 2014
  • July 2014
  • June 2014
  • May 2014
  • April 2014
  • March 2014
  • January 2014
  • November 2013
  • September 2013
  • August 2013
  • July 2013
  • Terms Of Use
  • Privacy Policy
  • Affiliate Disclaimer

Subscribe to the Coding Explorer Newsletter

* indicates required

Copyright © 2025 Wayne Media LLC · Powered by Genesis Framework · WordPress