Swift: Swift Sets and Dictionaries Tutorial

A Set is like a bag of colored marbles — one of each color, mixed together with no particular order. A Dictionary is like a phone book — look up a number quickly by name. This lesson covers both powerful data structures.

1. What You'll Learn


2. A Backend Engineer's Real Story

(1) Pain: 500 lines of if-else for user tag deduplication — painfully slow

Bob is building a user profiling system. Each user has multiple tags (e.g. "VIP", "PromoSensitive", "HighSpender") and he needs extensive set operations: find users who are both VIP and high spenders, merge old and new tags, exclude blacklisted tags. He started with arrays and loop-based deduplication:

SWIFT
let oldTags = ["VIP", "HighSpender", "NewUser"]
let newTags = ["VIP", "PromoSensitive", "HighSpender"]
var merged: [String] = []
for tag in oldTags + newTags {
    if !merged.contains(tag) {
        merged.append(tag)
    }
}

200,000 users x O(n^2) algorithm = server CPU maxed out for 20 minutes. Bob got complaints from ops.

(2) The Set and Dictionary Solution

SWIFT
let oldTags: Set = ["VIP", "HighSpender", "NewUser"]
let newTags: Set = ["VIP", "PromoSensitive", "HighSpender"]
let merged = oldTags.union(newTags)
let common = oldTags.intersection(newTags)
print("Merged: \(merged)")
print("Common: \(common)")

(3) Result: 20 minutes → 0.5 seconds

Metric Array Loop Set/Dictionary
200K user dedup 20 minutes 0.5 seconds
Lines of code 500+ 30
Memory usage 200 MB 45 MB
Union operation Hand-written loop .union() one line

3. Sets

A Set is an unordered collection of unique elements. Arrays care about "order and repetition"; Sets care about "uniqueness and membership."

100%
graph TB
    A[Set A] --- B["{1, 2, 3}"]
    C[Set B] --- D["{2, 3, 4}"]
    E[Union] --- F["{1, 2, 3, 4}"]
    G[Intersection] --- H["{2, 3}"]
    I[Symmetric Diff] --- J["{1, 4}"]
    K[Subtracting] --- L["A - B = {1}"]
Set Operation Swift Method Result
Union union(_:) All elements from both sets
Intersection intersection(_:) Elements shared by both sets
Difference subtracting(_:) Elements in A but not in B
Symmetric difference symmetricDifference(_:) Elements in exactly one set
Is subset isSubset(of:) All of A's elements are in B
Contains contains(_:) O(1) membership check

(1) Creating and Basic Operations

SWIFT
var fruits: Set<String> = ["Apple", "Banana", "Orange"]
fruits.insert("Apple")
fruits.insert("Grape")
fruits.remove("Banana")
print(fruits.contains("Apple"))
print(fruits.count)

(2) Set Operations

SWIFT
let a: Set = [1, 2, 3, 4, 5]
let b: Set = [4, 5, 6, 7, 8]
print("Union: \(a.union(b).sorted())")
print("Intersection: \(a.intersection(b).sorted())")
print("A - B: \(a.subtracting(b).sorted())")
print("Symmetric Diff: \(a.symmetricDifference(b).sorted())")

▶ Example: User Tag Management System

SWIFT
// ============================================
// Managing user tags with Sets
// ============================================
var userTags: Set<String> = ["VIP", "NewUser", "HighSpender"]
let campaignTags: Set = ["VIP", "PromoSensitive"]
let excludeTags: Set = ["Inactive", "Fraud"]
userTags.insert("iOSUser")
userTags.insert("VIP")
let targetUsers = campaignTags.subtracting(excludeTags)
print("Target tags: \(targetUsers)")
let vipHighSpender = userTags.intersection(["VIP", "HighSpender"])
print("VIP high spenders: \(vipHighSpender)")
let allActive = userTags.union(campaignTags).subtracting(excludeTags)
print("All active tags: \(allActive.sorted())")

Output:

TEXT 📖 Display only
Target tags: ["VIP", "PromoSensitive"]
VIP high spenders: ["VIP", "HighSpender"]
All active tags: ["HighSpender", "iOSUser", "NewUser", "PromoSensitive", "VIP"]

4. Dictionaries

A Dictionary is an unordered collection of key-value pairs, each key mapping uniquely to a value. Ideal for "look up by name" scenarios.

100%
graph TB
    A[Dictionary] --> B["Key: Apple -> Value: 3"]
    A --> C["Key: Banana -> Value: 5"]
    A --> D["Key: Orange -> Value: 2"]
    B --> E[O(1) lookup by key]
Operation Syntax Notes
Create KeyType: ValueType Empty dictionary
Literal ["a": 1, "b": 2] With initial values
Read dict["key"] Returns Optional
Assign dict["key"] = value Add or update
Delete dict["key"] = nil Remove the key-value pair
Traverse for (k, v) in dict Iterate all

(1) Creating and Modifying

SWIFT
var scores: [String: Int] = [:]
var populations = [
    "New York": 8_336_817,
    "Los Angeles": 3_979_576,
    "Chicago": 2_693_976
]
populations["Houston"] = 2_320_268
populations["New York"] = 8_400_000
populations["Chicago"] = nil
if let nyPopulation = populations["New York"] {
    print("NY population: \(nyPopulation)")
}

(2) Traversing Dictionaries

SWIFT
let capitals = [
    "USA": "Washington DC",
    "UK": "London",
    "Japan": "Tokyo",
    "France": "Paris"
]
for (country, city) in capitals {
    print("\(country): \(city)")
}
print("Countries: \(capitals.keys.sorted())")
print("Capitals: \(capitals.values.sorted())")

▶ Example: Inventory Management

SWIFT
// ============================================
// Managing inventory with Dictionary
// ============================================
var inventory: [String: Int] = [
    "Laptop": 15,
    "Mouse": 50,
    "Keyboard": 30
]
inventory["Monitor"] = 10
inventory["Mouse"]! += 20
if let laptopStock = inventory["Laptop"], laptopStock > 0 {
    inventory["Laptop"] = laptopStock - 1
    print("Shipped 1 Laptop")
}
let product = "Tablet"
if let stock = inventory[product] {
    print("\(product): \(stock) units")
} else {
    print("\(product) not found in inventory")
}
print("\n=== Inventory Report ===")
for (product, quantity) in inventory {
    let status = quantity < 20 ? "Low stock" : "In stock"
    print("\(product): \(quantity) units -- \(status)")
}

Output:

TEXT 📖 Display only
Shipped 1 Laptop
Tablet not found in inventory

=== Inventory Report ===
Laptop: 14 units -- Low stock
Mouse: 70 units -- In stock
Keyboard: 30 units -- In stock
Monitor: 10 units -- Low stock

5. Hashable Protocol and Nested Collections

Dictionary keys and Set elements must conform to the Hashable protocol — Swift uses hash values for fast lookup.

Built-in Hashable Types When Manual Conformance Is Needed
String, Int, Double, Bool Custom structs/classes
Array (with hashable elements) Enums with associated values
Set, Dictionary (as values) Types containing non-hashable properties

(1) Custom Types as Keys

SWIFT
struct Product: Hashable {
    let id: Int
    let name: String
}
var cart: [Product: Int] = [:]
let laptop = Product(id: 1001, name: "Laptop")
cart[laptop] = 2
print("Cart items: \(cart.count)")

(2) Nested Collections: Dictionary of Sets

SWIFT
var cityTags: [String: Set<String>] = [
    "Paris": ["Eiffel Tower", "Louvre"],
    "Tokyo": ["Shibuya", "Sensoji"]
]
cityTags["Paris"]?.insert("Arc de Triomphe")
cityTags["London"] = ["Big Ben", "Tower Bridge"]
for (city, landmarks) in cityTags {
    print("\(city): \(landmarks.sorted().joined(separator: ", "))")
}

▶ Example: User Group Statistics

SWIFT
// ============================================
// User group stats with Dictionary and Set
// ============================================
let userLanguages: [String: Set<String>] = [
    "Alice": ["Swift", "Python", "JavaScript"],
    "Bob": ["Python", "Java", "Go"],
    "Charlie": ["Swift", "Kotlin", "JavaScript"],
    "Diana": ["Java", "C#", "Python"]
]
let swiftUsers = userLanguages.filter { $0.value.contains("Swift") }
print("Swift developers: \(swiftUsers.count)")
let allLanguages = userLanguages.values.reduce([]) { $0.union($1) }
print("All languages: \(allLanguages.sorted())")
let fullStack = userLanguages.filter { $0.value.count >= 3 }
for (name, langs) in fullStack {
    print("Full stack: \(name) -- \(langs.sorted().joined(separator: ", "))")
}

Output:

TEXT 📖 Display only
Swift developers: 2
All languages: ["C#", "Go", "Java", "JavaScript", "Kotlin", "Python", "Swift"]
Full stack: Alice -- JavaScript, Python, Swift
Full stack: Charlie -- JavaScript, Kotlin, Swift
Full stack: Diana -- C#, Java, Python

6. Full Example: User Profile Tagging System

SWIFT
// ============================================
// User profile tagging system
// Combining Set and Dictionary concepts
// ============================================
import Foundation
struct UserProfile: Hashable {
    let id: Int
    let name: String
    var tags: Set<String>
}
var users: [Int: UserProfile] = [
    1: UserProfile(id: 1, name: "Alice", tags: ["VIP", "HighSpender", "iOS"]),
    2: UserProfile(id: 2, name: "Bob", tags: ["NewUser", "Android"]),
    3: UserProfile(id: 3, name: "Charlie", tags: ["VIP", "Android", "HighSpender"]),
    4: UserProfile(id: 4, name: "Diana", tags: ["iOS", "NewUser"])
]
let campaignTags: Set = ["VIP", "iOS"]
let excludeTags: Set = ["Fraud", "Inactive"]
var targetUserIds: Set<Int> = []
for (id, profile) in users {
    let effectiveTags = profile.tags.subtracting(excludeTags)
    if !effectiveTags.intersection(campaignTags).isEmpty {
        targetUserIds.insert(id)
    }
}
print("=== Campaign Target Users ===")
for id in targetUserIds.sorted() {
    if let user = users[id] {
        print("\(user.name) -- tags: \(user.tags.sorted().joined(separator: ", "))")
    }
}
print("\n=== Adding Tag: BetaTester ===")
for id in users.keys {
    users[id]?.tags.insert("BetaTester")
}
print("\n=== Tag Distribution ===")
var tagCounts: [String: Int] = [:]
for (_, profile) in users {
    for tag in profile.tags {
        tagCounts[tag, default: 0] += 1
    }
}
for (tag, count) in tagCounts.sorted(by: { $0.value > $1.value }) {
    print("\(tag): \(count) users")
}

Output:

TEXT 📖 Display only
=== Campaign Target Users ===
Alice -- tags: HighSpender, iOS, VIP
Charlie -- tags: Android, HighSpender, VIP
Diana -- tags: iOS, NewUser

=== Adding Tag: BetaTester ===

=== Tag Distribution ===
BetaTester: 4 users
Android: 2 users
HighSpender: 2 users
iOS: 2 users
NewUser: 2 users
VIP: 2 users

❓ FAQ

Q How do I choose between Set and Array?
A Use Set when you need uniqueness, fast lookup (O(1)), and don't care about order. Use Array when you need ordering, allow duplicates, and index-based access.
Q What does a Dictionary return when a key isn't found?
A It returns nil (Optional). Always handle dictionary lookups with if-let or the ?? operator.
Q What is Hashable, and why do Set and Dictionary keys need it?
A Hashable means the type can be hashed. Swift uses hash values to locate elements quickly (O(1) complexity). Int, String, and other basic types conform to Hashable by default.
Q What should I watch out for when using a custom struct as a dictionary key?
A It must conform to Hashable. If all stored properties are Hashable, Swift auto-synthesizes the hash method. Otherwise, implement hash(into:) and == manually.
Q What do the keys and values properties of Dictionary return?
A They return collection types — keys is Dictionary.Keys (usable like a Set), values is Dictionary.Values (usable like an Array). Both can be traversed directly.

📖 Summary


📝 Exercises

  1. Beginner: Create a Set of your 5 favorite books. Check if "Swift Programming" is in it, add 2 new books, and print the final set.
  2. Intermediate: Implement a simple English-Chinese dictionary using Dictionary. Add 5 words with translations, implement a lookup function (input English, return Chinese), and handle cases where a word is not found.
  3. Challenge: Analyze a dataset: ["Alice": ["Swift", "Python"], "Bob": ["Java", "Swift"], "Charlie": ["Python", "Go"], "Diana": ["Swift", "Go"]]. Find users who know Swift but not Go, users who know Python or Java, the set of all languages (deduplicated), and the user count per language.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏