# Swift 1.2: Let's Talk About Sets, Baby!

Join the DZone community and get the full member experience.

Join For Free*Set*. Even if you’ve never come across the type before, you may well have hand coded the functionality of a set when you’ve wanted to, for example, compare two arrays to find the commonality between them. Or you may have used similar functionality when you’ve used unions and joins in SQL.

let odd = Set([1, 3, 3, 3, 5, 7, 9]) // {5, 7, 3, 1, 9} let even = Set([2, 2, 2, 2, 2, 2, 4, 6, 8, 8, 10]) // {6, 10, 2, 4, 8}

*isDisjoint()*returns a

*Boolean*indicating that no members exist in both sets:

let oddDisjointWithEven = odd.isDisjointWith(even) // true

To combine these sets together, we’ll use the *union()* method to give us a new *Set* instance named *allNumbers*:

let allNumbers = odd.union(even) // {10, 2, 9, 4, 5, 7, 6, 3, 1, 8}

To do some more tinkering, I’ll create my own set named *xyzzyNumbers*:

let xyzzyNumbers = Set([1, 2, 3, 4]) // {2, 3, 1, 4}

…this new set does contain some odd numbers, so I’d expect *isDisjoint()* again my odd numbers to be false:

let xyzzyDisjointWithOdd = xyzzyNumbers.isDisjointWith(odd) // false

To get the odd numbers in *xyzzy* that are below ten, I can use the *intersect()* method:

let xyzzyOddNumbers = xyzzyNumbers.intersect(odd) // {3, 1}

…and likewise with even:

let xyzzyEvenNumbers = xyzzyNumbers.intersect(even) // {2, 4}

The is*Subset()* method returns true if one set is a subset of the other. *xyzzy* isn’t a subset of *odd, *because it contains even numbers, however it is a subset of *allNumbers*:

let xyzzyIsSubsetOfOdd = xyzzyNumbers.isSubsetOf(odd) // false let xyzzyIsSubSetOfAll = xyzzyNumbers.isSubsetOf(allNumbers) // true

…and this implies that *allNumbers* is a superset of *xyzzy*:

let allIsSupersetOfXyzzy = allNumbers.isSupersetOf(xyzzyNumbers) // true

I can also subtract one set from another. Here, I remove all of the numbers from *xyzzy* from my odd numbers:

let oddNumbersSubtractXyzzy = odd.subtract(xyzzyNumbers) // {5, 7, 9}

Sets aren’t limited to integers, they can be created from any hashable types, such as strings:

let colors = Set(["red", "orange", "purple"]) // {"orange", "purple", "red"} let fruit = Set(["apple", "banana", "orange"]) // {"banana", "apple", "orange"} let fruityColors = colors.intersect(fruit) // {"orange"} let nonFruityColors = colors.exclusiveOr(fruit) // {"banana", "purple", "red", "apple"}

But types can’t be mixed, so this will not compile:

let evenColors = colors.intersect(even)

**Addendum - Creating Sets From Your Own Data Types**

If you want to create sets of your own data type, you need to ensure your struct or class conforms to the *Hashable* protocol which extends the *Equatable* protocol. This means you’ll need your data data to implement an equality function (‘==‘) and be able to return a hashed value of itself.

I’ve created a simple *Creature *struct with a handful of properties that describe a creature:

let name: String let swims: Bool let flies: Bool let walks: Bool let legCount: Int

To conform to *Hashable*, *Creature*’s *hashValue* returns the integer hash value of its *name* property (*String* is already *Hashable*, so that’s built in) and adds increasing powers of ten for the three Boolean properties and finally adds the number of legs:

var hashValue: Int { get { return name.hashValue + (swims ? 10 : 0) + (flies ? 100 : 0) + (walks ? 1000 : 0) + legCount } }

To conform to *Equatable, *we implement an equality function (thanks to Davide De Franceschi for pointing out hash value is not a good way to equate two objects!):

func == (lhs: Creature, rhs: Creature) -> Bool { return lhs.name == rhs.name && lhs.swims == rhs.swims && lhs.flies == rhs.flies && lhs.walks == rhs.walks && lhs.legCount == rhs.legCount }

The final *Creature* definition looks like:

struct Creature: Hashable { init(name: String, swims: Bool, flies: Bool, walks: Bool, legCount: Int) { self.name = name self.swims = swims self.flies = flies self.walks = walks self.legCount = legCount } let name: String let swims: Bool let flies: Bool let walks: Bool let legCount: Int var hashValue: Int { get { return name.hashValue + (swims ? 10 : 0) + (flies ? 100 : 0) + (walks ? 1000 : 0) + legCount } } }

We can now create an array of a handful of different creatures:

…and then create some sets of creatures with different special skills:

let ant = Creature(name: "Ant", swims: false, flies: false, walks: true, legCount: 6) let bumbleBee = Creature(name: "Bumble Bee", swims: false, flies: true, walks: false, legCount: 6) let cat = Creature(name: "Cat", swims: false, flies: false, walks: true, legCount: 4) let flyingFish = Creature(name: "Flying Fish", swims: true, flies: true, walks: false, legCount: 0) let human = Creature(name: "Human", swims: true, flies: false, walks: true, legCount: 2) let penguin = Creature(name: "Penguin", swims: true, flies: false, walks: true, legCount: 2) let swift = Creature(name: "Swift", swims: false, flies: true, walks: true, legCount: 2) let creaturesArray = [ant, ant, ant, bumbleBee, cat, flyingFish, human, penguin, swift] let swimmers = Set(creaturesArray.filter({ $0.swims })) let walkers = Set(creaturesArray.filter({ $0.walks })) let fliers = Set(creaturesArray.filter({ $0.flies }))

Using the *Set* syntax, we can find the creatures that can fly and walk:

let flyingWalkers = fliers.intersect(walkers)

*Set*, although there were three little ants in the source array, there’s only one in the set.

We can find creatures that can walk but can’t fly or swim by chaining two set methods together:

let walkingNonSwimmersNonFliers = walkers.subtract(swimmers).subtract(fliers)

…or we can find the creatures that can either fly or swim:

let fliersAndSwimmers = fliers.union(swimmers)

Again, although the flying fish can do both, because we’re dealing with sets, it only appears once.

Published at DZone with permission of Simon Gladman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Comments