Skip to content

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

var a = [1, 2, 3]
var b = a
b.append(4)
print(a)   // [1, 2, 3]
print(b)   // [1, 2, 3, 4]

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], or array[0] on an empty array. Use first/last.
  • Declaring with let and then calling append — compile error; use var.
  • Removing while iterating by index — indices shift. Use removeAll(where:) or filter.
  • Forgetting that ArraySlice keeps original indices.
  • Using sort() when you wanted sorted() (or vice versa) — the first returns nothing.
  • Confusing map with forEach: use map when you want the results; for-in is usually clearer for side effects.

Exercises

  1. Given [5, 3, 8, 1], print the sum, the average, and the largest value without writing a manual loop.
  2. Remove all duplicates from [3, 1, 3, 2, 1] while keeping the first occurrence order.
  3. Rotate an array left by one position: [1,2,3,4] → [2,3,4,1].
  4. Given an array of names, print them sorted by length, then alphabetically for ties.
  5. Merge two sorted arrays into one sorted array without calling sort().
  6. Build a 3×3 identity matrix as a [[Int]] and print it row by row.
  7. 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/isEmpty are safe.
  • append, insert, remove(at:), removeAll(where:) need a var.
  • map / filter / reduce / compactMap transform collections; sorted() vs sort() returns vs mutates.
  • Slices share the original's indices — wrap in Array(...).

Next: Dictionaries & sets →