Skip to content
AgileLogics
6 min read

Working with Collections

Prerequisites: map, filter, and reduce, Structs

Real programs spend much of their time sorting, grouping, and searching collections of structs. Swift's collection methods make this concise.

struct Order {
    let customer: String
    let total: Double
    let isPaid: Bool
}

let orders = [
    Order(customer: "Ada", total: 120, isPaid: true),
    Order(customer: "Linus", total: 45, isPaid: false),
    Order(customer: "Ada", total: 80, isPaid: true),
    Order(customer: "Grace", total: 300, isPaid: true),
]

Sorting by a property

let byTotal = orders.sorted { $0.total > $1.total }
print(byTotal.map(\.customer))
["Grace", "Ada", "Ada", "Linus"]

Searching

print(orders.first(where: { !$0.isPaid })?.customer ?? "none")
print(orders.contains { $0.total > 250 })
print(orders.firstIndex(where: { $0.customer == "Grace" }) ?? -1)
print(orders.filter(\.isPaid).count)
Linus
true
3
3

Grouping and totaling

Dictionary(grouping:by:) groups elements by a key:

let byCustomer = Dictionary(grouping: orders, by: \.customer)

for customer in byCustomer.keys.sorted() {
    let spent = byCustomer[customer, default: []].map(\.total).reduce(0, +)
    print("\(customer): \(spent)")
}
Ada: 200.0
Grace: 300.0
Linus: 45.0

Combining sequences

let names = ["Ada", "Linus", "Grace"]
let scores = [92, 78, 88]

for (name, score) in zip(names, scores) {
    print("\(name): \(score)")
}

let lookup = Dictionary(uniqueKeysWithValues: zip(names, scores))
print(lookup["Grace"] ?? 0)
Ada: 92
Linus: 78
Grace: 88
88

zip pairs up two sequences element by element.

Slices

prefix, suffix, dropFirst, and ranges like orders[1...2] return an ArraySlice — a lightweight view into the original array. Wrap it in Array(...) if you want to keep it around independently.

Practice

Using the orders array, print the names of customers with unpaid orders, and the average total of paid orders.

Show solution
let unpaid = orders.filter { !$0.isPaid }.map(\.customer)
print(unpaid)

let paid = orders.filter(\.isPaid)
let average = paid.map(\.total).reduce(0, +) / Double(paid.count)
print(average)
["Linus"]
166.66666666666666