7. Arrays¶
What & why¶
An array is an ordered, growable list of values of the same type. It's the collection you'll use most. Arrays in Swift are value types: copying one gives you an independent list.
Creating arrays¶
let primes = [2, 3, 5, 7] // [Int]
var names: [String] = [] // empty, type annotated
var zeros = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0]
let letters = Array("abc") // ["a", "b", "c"] ([Character])
let evens = Array(stride(from: 0, to: 10, by: 2)) // [0, 2, 4, 6, 8]
[Int] is shorthand for Array<Int>.
Accessing elements¶
let fruits = ["apple", "banana", "cherry"]
print(fruits[0]) // apple
print(fruits.count) // 3
print(fruits.first!) // apple (first/last are optional: nil when empty)
print(fruits.last ?? "-") // cherry
print(fruits.isEmpty) // false
// print(fruits[3]) // fatal error: Index out of range
Index out of range crashes — Swift won't read garbage memory. Use first, last, or check indices.contains(i) when unsure.
Modifying (needs var)¶
var todo = ["write", "test"]
todo.append("ship") // ["write", "test", "ship"]
todo += ["celebrate", "rest"] // append several
todo.insert("plan", at: 0) // at the front
let removed = todo.remove(at: 1) // returns the removed element ("write")
let last = todo.removeLast() // "rest"
todo[0] = "design" // replace
todo.removeAll { $0 == "celebrate" } // remove by predicate
print(todo)
append/removeLast are fast; insert(at: 0)/remove(at: 0) shift every element (O(n)).
Slices and ranges¶
let nums = [10, 20, 30, 40, 50]
print(nums[1...3]) // [20, 30, 40] — an ArraySlice
print(nums[..<2]) // [10, 20]
print(nums.prefix(2)) // [10, 20]
print(nums.suffix(2)) // [40, 50]
print(Array(nums[1...3])) // convert slice to a real Array
Slice indices
An ArraySlice keeps the original array's indices. nums[2...4][2] is 30, not 50. Wrap in Array(...) if you want to start at 0 again.
Iterating¶
for n in nums { print(n) }
for (i, n) in nums.enumerated() { print(i, n) }
for i in nums.indices { print(nums[i]) }
Transforming: map, filter, reduce, and friends¶
let words = ["swift", "is", "fun"]
print(words.map { $0.uppercased() }) // ["SWIFT", "IS", "FUN"]
print(words.map(\.count)) // [5, 2, 3] — key path as function
print(words.filter { $0.count > 2 }) // ["swift", "fun"]
print(words.reduce(0) { $0 + $1.count }) // 10
print(words.joined(separator: " ")) // swift is fun
print([[1, 2], [3]].flatMap { $0 }) // [1, 2, 3]
print(["1", "x", "3"].compactMap { Int($0) })// [1, 3] — drops nils
print(words.contains("fun")) // true
print(words.contains { $0.hasPrefix("i") }) // true
print(words.firstIndex(of: "is") ?? -1) // 1
print(words.allSatisfy { !$0.isEmpty }) // true
The { ... } bits are closures — small anonymous functions. $0 is the first argument. We'll study them in Intermediate; for now, just use them.
Sorting¶
var scores = [42, 7, 19, 88]
scores.sort() // in place: [7, 19, 42, 88]
let desc = scores.sorted(by: >) // new array: [88, 42, 19, 7]
let byLength = ["ccc", "a", "bb"].sorted { $0.count < $1.count } // ["a", "bb", "ccc"]
scores.reverse() // in place
let r = scores.reversed() // lazy view; wrap in Array(...) if you need an Array
print(scores.min()!, scores.max()!) // min()/max() return optionals
sort() returns Void (in place); sorted() returns a new array. The names tell you which: the -ed / -ing form returns a copy.
Value semantics: copies are independent¶
Swift copies lazily (copy-on-write), so this is cheap until one side mutates.
Multidimensional arrays¶
var grid = Array(repeating: Array(repeating: 0, count: 3), count: 2) // 2 rows x 3 cols
grid[1][2] = 9
print(grid) // [[0, 0, 0], [0, 0, 9]]
Fixed-size and other sequence-like types¶
You'll mostly use Array. Others exist for special needs: Set and Dictionary (next lesson), ContiguousArray, and (since Swift 6.2) InlineArray for fixed-size storage. Reach for them only when you have a reason.
Common mistakes¶
- Index out of range:
array[array.count], orarray[0]on an empty array. Usefirst/last. - Declaring with
letand then callingappend— compile error; usevar. - Removing while iterating by index — indices shift. Use
removeAll(where:)orfilter. - Forgetting that
ArraySlicekeeps original indices. - Using
sort()when you wantedsorted()(or vice versa) — the first returns nothing. - Confusing
mapwithforEach: usemapwhen you want the results;for-inis usually clearer for side effects.
Exercises¶
- Given
[5, 3, 8, 1], print the sum, the average, and the largest value without writing a manual loop. - Remove all duplicates from
[3, 1, 3, 2, 1]while keeping the first occurrence order. - Rotate an array left by one position:
[1,2,3,4]→[2,3,4,1]. - Given an array of names, print them sorted by length, then alphabetically for ties.
- Merge two sorted arrays into one sorted array without calling
sort(). - Build a 3×3 identity matrix as a
[[Int]]and print it row by row. - Write
chunks(of:)yourself: split[1,2,3,4,5]into[[1,2],[3,4],[5]].
Recap¶
- Arrays are ordered, typed, growable, and value types.
- Out-of-range indexing crashes;
first/last/isEmptyare safe. append,insert,remove(at:),removeAll(where:)need avar.map/filter/reduce/compactMaptransform collections;sorted()vssort()returns vs mutates.- Slices share the original's indices — wrap in
Array(...).
Next: Dictionaries & sets →