Use Sets in Swift

Of the most underrated collection types is sets. Sets are an unordered collection with unique values. Let’s take a look at what you can do with sets. 

Download the exercise files and you’ll find I created  playground with a Struct called Pizza. I’ve made two pizzas for you. Currently, this is all arrays, but does it have to be arrays? Ask yourself three questions:

  • Do I need ordered values?
  • Do I need unique values
  • Do I need access directly to any value? 

If you need ordered values and direct access, you’ll want an array. If you want unique values and don’t care about order or access, you’ll want a set. A Pizza is a good example of where a set works well. An ingredient is an ingredient. There is no good reason to list it twice, and it also has no particular order for toppings. I don’t need to access only one ingredient.  That’s a set. Sets are stored and accessed more eifficiently than array or dictionaries, so if you can use a set, do so. 

Lets convert this struct to sets. For the two constants on top, I add Set and the compiler will implicitly make this a set of strings. For cheese and toppings, I have to get more explicit. 

var cheeses: Set<String>
var toppings: Set<String>

I added the type of the set in angle brackets. Run what you got and it still works.  The pizzas below still compile. 

You can enumerate a set using for. For example: 

for cheese in quattroFormaggi.cheeses{
    print(cheese)
}

You are not guaranteed the order however.  If I want them sorted, I can use the sorted method

for cheese in quattroFormaggi.cheeses.sorted(){
    print(cheese)
}

The core of sets is membership testing. I can check for the existence of something easily. 

chicago.toppings.contains("Sausage")

returns true

quattroFormaggi.toppings.contains("Sausage")

Return false.

I can look for a bigger set too. I have a set of toppings here. I can check it against one of my pizzas. 

 margheritaDoc.toppings.isSubset(of: toppings)

That comes out false. I can find the missing ingredients quickly. 

margheritaDoc.toppings.subtracting(toppings)

And I find I have whole tomatoes instead of crushed here. 

Are any authentic  cheeses also toppings?  I can use an intersection to check that.

margheritaDoc.toppings.intersection(margheritaDoc.authenticCheese)

That gives me one case. If I want true or false I can use isEmpty. To look for the empty set. 

margheritaDoc.toppings.intersection(margheritaDoc.authenticCheese).isEmpty

What if I want to check against two pizzas. I can union the pizzas and check that against the list. 

let cheeses =  quattroFormaggi.cheeses.union(margheritaDoc.cheeses)
cheeses.intersection(quattroFormaggi.authenticCheese)

So What of the two lists are excluded?  

cheeses.symmetricDifference(quattroFormaggi.authenticCheese)

 I for one would consider all that Authentic. 

In this simple example you can see that if you don’t care about order and have unique values, Sets may be a an efficient way of storing data.. 

The Whole Code

You can download this Swift playground from GitHub here: Tips_08_01_Sets_End

struct Pizza{
    let authenticCheese: Set = ["Bufalo","Fior de late","Gorgonzola","Mozzarella","Parmigiano"]
    let authenticIngredients: Set = ["Basil","Peppers","Tomatoes", "Basil", "Oregano"]
    enum Crust{
        case thin,thick,pan,lavosh,potPie
    }

    var crust : Crust
    var cheeses: Set<String>
    var toppings: Set<String>
}

let margherita = Pizza(crust: .thin, cheeses: ["Mozzarella"], toppings: ["Basil","Tomatoes","Parmigiano","Oil"])

let margheritaDoc = Pizza(crust: .thin, cheeses: ["Bufala"], toppings: ["Basil","Tomatoes","Parmigiano","Oil"])

let chicago = Pizza(crust: .pan, cheeses: ["Mozzarella"], toppings: ["Pizza Sauce","Sausage"])

let quattroFormaggi = Pizza(crust: .thin, cheeses: ["Fontina","Gorgonzola","Mozzarella","Parmigiano"], toppings: ["Crushed Tomatoes","Basil","Oil"])

for cheese in quattroFormaggi.cheeses.sorted(){
    print(cheese)
}

let toppings: Set = ["Crushed Tomatoes","Basil","Oil","Chicken","BBQ Sauce","Red Onions","Parmigiano", "Peppernoi","Prociutto","Pineapple","Canadian Bacon"]

chicago.toppings.contains("Sausage")

quattroFormaggi.toppings.contains("Sausage")

margheritaDoc.toppings.isSubset(of: toppings)

margheritaDoc.toppings.subtracting(toppings)

margheritaDoc.toppings.intersection(margheritaDoc.authenticCheese).isEmpty

let cheeses =  quattroFormaggi.cheeses.union(margheritaDoc.cheeses)

cheeses.intersection(quattroFormaggi.authenticCheese)

cheeses.symmetricDifference(quattroFormaggi.authenticCheese)




	
	

	

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: