iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Dictionaries

Swift dictionaries are typed key/value maps. Hash-based; lookup is O(1) average. Use brackets to access, keys / values to iterate, updateValue to swap.

Create, access, iterate, transform

EXAMPLE
// 1) Create
var scores: [String: Int] = ["Ada": 95, "Bo": 88, "Cy": 92]
var empty: [String: Int] = [:]
var inferred = ["name": "Ada", "role": "admin"]   // [String: String]

let user: [String: Any] = [
    "name":  "Ada",
    "age":   32,
    "admin": true,
]

// 2) Read — subscript returns Optional
let bo: Int? = scores["Bo"]                       // Optional(88)
let di: Int? = scores["Di"]                       // nil

// Default value
let score = scores["Di"] ?? 0                     // 0
let score2 = scores["Di", default: 0]             // 0 (Swift 4+)

// 3) Modify
scores["Di"] = 70                                 // insert
scores["Ada"] = 96                                // update
scores["Bo"] = nil                                // remove
scores.removeValue(forKey: "Cy")                  // remove + return old value

// updateValue returns the OLD value (or nil if new)
let old = scores.updateValue(75, forKey: "Di")

// 4) Iterate
for (name, score) in scores {
    print("\\(name): \\(score)")
}

for name in scores.keys {
    print(name)
}

for score in scores.values {
    print(score)
}

// Sorted iteration (dictionaries are unordered)
for (name, score) in scores.sorted(by: { $0.value > $1.value }) {
    print("\\(name): \\(score)")
}

// 5) Functional — map / filter / reduce
let doubled = scores.mapValues { $0 * 2 }       // [String: Int]
let passing = scores.filter { $0.value >= 90 }  // dictionary
let total = scores.values.reduce(0, +)            // sum

// Transform keys + values
let uppercaseNames = Dictionary(
    uniqueKeysWithValues: scores.map { ($0.key.uppercased(), $0.value) }
)

// Or use reduce(into:) for accumulation
let bins = scores.reduce(into: [String: [String]]()) { result, entry in
    let bin = entry.value >= 90 ? "A" : "B"
    result[bin, default: []].append(entry.key)
}

// 6) Sequence → Dictionary
let users = [User(id: 1, name: "Ada"), User(id: 2, name: "Bo")]
let byId = Dictionary(uniqueKeysWithValues: users.map { ($0.id, $0) })
// [1: User(...), 2: User(...)]

// Group by key (multiple values per key)
let byCity = Dictionary(grouping: users, by: { $0.city })
// ["Sydney": [User, User], "Brisbane": [User]]

// Merge — handle key conflicts
let a = ["a": 1, "b": 2]
let b = ["b": 20, "c": 3]
let merged = a.merging(b, uniquingKeysWith: { current, new in current + new })
// ["a": 1, "b": 22, "c": 3]

// 7) Count
scores.count                                       // 4
scores.isEmpty                                     // false

// 8) Contains?
scores.keys.contains("Ada")                        // true
scores.values.contains(95)                         // true
scores["Ada"] != nil                               // true

// 9) Multi-level nested update
struct Person {
    var name: String
    var address: [String: String]
}

var people: [Int: Person] = [
    1: Person(name: "Ada", address: ["city": "Sydney"]),
]

people[1]?.address["city"] = "Melbourne"           // works because dict values aren't COW-shared

// 10) Pattern: counting occurrences
let words = ["hi", "hello", "hi", "go", "hello", "hi"]
var counts: [String: Int] = [:]
for w in words {
    counts[w, default: 0] += 1
}
// ["hi": 3, "hello": 2, "go": 1]

// 11) Pattern: index by
let productById = Dictionary(uniqueKeysWithValues: products.map { ($0.id, $0) })

// 12) Pattern: cache
struct Cache<K: Hashable, V> {
    private var store: [K: V] = [:]
    mutating func get(_ key: K, _ compute: () -> V) -> V {
        if let v = store[key] { return v }
        let v = compute()
        store[key] = v
        return v
    }
}

var cache = Cache<Int, String>()
let name = cache.get(42) { fetchNameFromDb(id: 42) }

// 13) Codable — dictionaries serialise to / from JSON
let json = try JSONEncoder().encode(scores)
let back = try JSONDecoder().decode([String: Int].self, from: json)

// 14) Convert to / from arrays
let keys   = Array(scores.keys)
let values = Array(scores.values)
let pairs  = scores.map { ($0.key, $0.value) }   // [(String, Int)]

// 15) Performance
// - Lookups, insertions, deletions: average O(1)
// - Iteration: ordered NOT GUARANTEED — sort if order matters
// - Hash collisions degrade to O(N) worst case — rare
// - Key type MUST conform to Hashable

// 16) Custom Hashable key types
struct UserId: Hashable {
    let id: Int
    let realm: String
}

var users: [UserId: User] = [:]
users[UserId(id: 42, realm: "prod")] = User(...)

// Auto-synthesised Hashable works when all fields are Hashable.

// 17) When dictionaries aren't enough
// - Need ordered insertion       → OrderedDictionary (swift-collections)
// - Need many keys for one value → reach for [K: [V]]
// - Need value comparable + sorted → [K: V] + sort, or SortedDictionary
// - Need concurrent access       → DispatchQueue serial + plain dict, or actor + dict

// 18) Common bugs
//   • Subscript returns Optional — easy to forget the `?`
//   • Forgetting Hashable on key type → compile error
//   • Mutating a dictionary while iterating → undefined
//   • Dictionary iteration order changes across runs — don't assume order
//   • Comparing `nil` to absence vs `nil` to stored nil value (for [K: V?])

Why it matters

Swift dictionaries are value types with O(1) lookup. updateValue returns the old; removeValue returns it too. Dictionary(grouping:by:) is the cleanest way to bucket items by key.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
var ages = ["Ada": 36, "Bo": 28]
ages["Cy"] = 22
for (name, age) in ages { print("\(name) = \(age)") }
Try it Yourself »

Discussion

Loading…