Diferrence between Set and OptionSet in Swift

Updated Jan 13, 2024#swift#sets

Set and OptionSet in Swift are both types that can represent a collection of distinct values. However, they have some differences:

  • Set is a generic type that can store any type that conforms to the Hashable protocol, such as Int, String, or custom types. OptionSet is a protocol that can be adopted by custom types that store a fixed-size sequence of bits, such as Int or UInt8.
  • Set uses hashing to store and access its elements efficiently, while OptionSet uses bitwise operations to manipulate its bits.
  • Set can have any size and grow dynamically as new elements are added or removed, while OptionSet has a fixed size determined by the raw value type.

The main difference between Set and OptionSet is their intended usage and the way they handle elements. Sets are used for managing collections of unique elements with no duplicates, while OptionSets are specifically designed for representing combinations of distinct options or flags using bit-wise operations.

Set

Set in Swift is an unordered collection of unique data of the same type. It can store any type that conforms to the Hashable protocol.

It is implemented using a hash table, which allows for fast membership tests and prevents duplicate elements. Sets in Swift are similar to mathematical sets, and they do not have a defined order for their elements.

You can perform various set operations, such as adding and removing elements, checking for membership, and performing set operations like union, intersection, and difference.

// Create a set of fruits
var fruits: Set<String> = ["Apple", "Banana", "Orange"]

// Add a new fruit to the set
fruits.insert("Grape")

// Print the set
print(fruits) // ["Banana", "Orange", "Grape", "Apple"]

// Check if the set contains "Mango"
print(fruits.contains("Mango")) // false

// Remove "Banana" from the set
fruits.remove("Banana")

// Print the set again
print(fruits) // ["Orange", "Grape", "Apple"]

// Create another set of fruits
let moreFruits: Set<String> = ["Mango", "Pineapple", "Apple"]

// Find the union of the two sets
let allFruits = fruits.union(moreFruits)

// Print the union set
print(allFruits) // ["Orange", "Grape", "Mango", "Pineapple", "Apple"]

// Find the intersection of the two sets
let commonFruits = fruits.intersection(moreFruits)

// Print the intersection set
print(commonFruits) // ["Apple"]

Since sets do not have a defined order, you cannot access elements by their index. Sets are useful when you need to work with a collection of unique elements and perform fast membership checks. They are particularly helpful in solving problems where you need to ensure uniqueness or remove duplicates from a sequence of elements.

OptionSet

A bitset is an array of bools that optimizes the space by storing each boolean value in one bit only. It is a common data structure in many programming languages, such as C++, Java, and Kotlin. A bitset can be used to represent a set of options, flags, or states efficiently and perform bitwise operations on them.

In Swift, you can use the OptionSet protocol to create a bitset type. You need to define a rawValue property of a type that conforms to the FixedWidthInteger protocol, such as Int or UInt8. You also need to define static properties for each option using unique powers of two (1, 2, 4, 8, etc.) for their raw values. You can then use an array literal to create an instance of your option set type and use set-related methods and operators to manipulate it.

You can then create an instance of OptionSet by assigning one or more of its static members to a variable or constant. You can also use set-related operations to check for membership and to add or remove members from an instance of your custom option set type.

// Define an option set for pizza toppings
struct PizzaToppings: OptionSet {
  // The raw value type
  let rawValue: UInt8

  // The options
  static let cheese = PizzaToppings(rawValue: 1 << 0) // 00000001
  static let pepperoni = PizzaToppings(rawValue: 1 << 1) // 00000010
  static let mushrooms = PizzaToppings(rawValue: 1 << 2) // 00000100
  static let olives = PizzaToppings(rawValue: 1 << 3) // 00001000

  // Some predefined combinations
  static let veggie: PizzaToppings = [.cheese, .mushrooms, .olives] // 00001101
  static let meatLovers: PizzaToppings = [.cheese, .pepperoni] // 00000011
}

// Create an instance of the option set using an array literal
var myPizza: PizzaToppings = [.cheese, .olives]

// Check if the option set contains an option
print(myPizza.contains(.pepperoni)) // false

// Add an option to the option set
myPizza.insert(.pepperoni)

// Print the raw value of the option set
print(myPizza.rawValue) // 11

// Remove an option from the option set
myPizza.remove(.olives)

// Print the option set again
print(myPizza) // [cheese, pepperoni]