Sets in Swift

swift sets

Sets are one of Swift’s collection types. A set stores an amount of unique values, that have no particular order. You can imagine a set to be like a box of billiard balls: They are all unique in terms of color and number, but they don’t have a certain order inside the box.

Hint: This post is using Swift 4 and Xcode 10

[toc]

Creating sets

Creating a set is very straightforward:

let setA: Set<String> = ["a","b","c"]

In this example, a set called setA  of strings has been created. It stores the values a, b  and c. In contrast to an array, the elements are not ordered. By using type interference, the set can also be created like this:

let setB: Set = ["a","b","c"]

It’s also possible to use the initialiser of the set type:

let setC = Set(["a","b","c"])

Like an array, a set is not mutable, if it’s declared as a constant by using let. For mutable sets, you can use the keyword var:

var setD = Set(["a","b"])

We will learn more about mutable sets later on.

Accessing the values of a set

You can access the values of a set by using a loop:

for value in setA {
     print(value)
}

Note that the order of the values in the loop can be different every time you run the code. From the outside it looks like they the values are picked randomly.

Analysing sets

First of all, you can check whether a set is empty:

print(setA.isEmpty)

Also the number of elements of a set can be checked:

print(setA.count)

Both of these operations are also available for arrays. More typical for a set is the question, whether a certain element is a member of a set. For that, you can use the method contains:

print(setA.contains("a"))

Adding and removing values from a set

Mutable sets can be changed. You can add and remove values:

setD.insert("c")
setD.remove("a")

Since the values of a set are unique, a value can only be added once to the same set. The insert method can be called with the same values multiple times, but the set won’t change:

var setE: Set = [1,2,3,4]

setE.insert(5)
setE.insert(5)
setE.insert(5)

print(setE) //[4,5,1,2,3]

As before, the order of the output can be different every time you run the code since sets are not ordered.

Comparing sets

Sets can be compared. Obviously, comparing the equality of two sets is possible:

let setA: = [“a”, “b”, “c”]
let setB: = [“a”, “b”, “c”]

if setA == setB {
     print(“the sets are equal”)
}

In this case, the sets are equal.

There is no useful definition for a smaller and bigger comparison of two sets, but you can check whether a set is a subset of another set:

let intSetA: Set = [1,2,3,4,5,6,7,8,9,0]
let intSetB: Set = [1,2,3]
intSetB.isSubset(of: intSetA) //true

It’s also possible to check whether it’s a strict subset. In that case, it has to be a subset but must not be equal:

let intSetA: Set = [1,2,3,4,5,6,7,8,9,0]
let intSetB: Set = [1,2,3,4,5,6,7,8,9,0]
let intSetC: Set = [3,4,5]

intSetB.isSubset(of: intSetA) //true
intSetB.isStrictSubset(of: intSetA) //false
intSetC.isSubset(of: intSetA) // true
intSetC.isStrictSubset(of: intSetA) //true

The opposite principle is a superset:

let intSetA: Set = [1,2,3,4,5,6,7,8,9,0]
let intSetC: Set = [3,4,5]
intSetA.isSuperset(of: intSetC) //true
intSetA.isStrictSuperset(of: intSetC) //true

Two sets are disjoint, if they don’t have any element in common:

let intSetA: Set = [1,2,3,4,5,6,7,8,9,0]
let intSetC: Set = [3,4,5]
let intSetD: Set = [13,14,15]

intSetA.isDisjoint(with: intSetC) //false
intSetA.isDisjoint(with: intSetD) //true

Combining sets

You can combine two sets to create a new one. A union of two sets contains all elements from both sets:

let stringSetA: Set = ["a","b","c"]
let stringSetB: Set = ["c","d","e"]

let unionSetAB = stringSetA.union(stringSetB)
print(unionSetAB) //["d", "b", "c", "a", "e"]

An intersection on the other hand contains only the elements, that are in both sets:

let stringSetA: Set = ["a","b","c"]
let stringSetB: Set = ["c","d","e"]

let intersectionAB = stringSetA.intersection(stringSetB)
print(intersectionAB) //[“c”]

Using sets with custom types

Of course you can also store values of your own type in a set. This type could be for example a struct or a class. However, in order for the set to work properly, this type has to conform to the hashable protocol.

class Movie: Hashable {

     var title: String
     var year: Int

     init(title: String, year: Int) {
          self.title = title
          self.year = year
     }

     static func == (lhs: Movie, rhs: Movie) -> Bool {
          return lhs.title == rhs.title &&
          lhs.year == rhs.year
     }

     var hashValue: Int {
          return title.hashValue ^ year.hashValue
     }

}

let terminator = Movie(title: "Terminator", year: 1980)
let backToTheFuture = Movie(title: "Back to the Future", year: 1985)

let movieSetA: Set = [terminator,backToTheFuture]

References:

Title image: @ LaineN / shutterstock.com