Overview

Swift collection methods live on the Sequence and Collection protocols, so they work on Array, Set, Dictionary, String, and any custom type that conforms. All methods in the standard library are non-mutating by default; mutating variants typically carry a different name (sort() mutates, sorted() does not). Chaining methods produces lazy sequences only when you call .lazy first; otherwise each transformation creates an intermediate array. For value semantics and copy-on-write behavior, see swift-value-types.

Transformation methods

Non-mutating; return a new collection.

MethodInputReturnsNotes
map(_:)(Element) -> T[T]Transform every element; always same count.
compactMap(_:)(Element) -> T?[T]Map and drop nil results; flattens optionals.
flatMap(_:)(Element) -> Sequence[T]Map and flatten one level; equivalent to map().joined().
filter(_:)(Element) -> Bool[Element]Keep elements where closure returns true.
reduce(_:_:)(Result, Element) -> ResultResultFold to a single value; always provide an initial value.
reduce(into:_:)(inout Result, Element)ResultMore efficient for appending to containers; avoids copies.
let nums = [1, 2, 3, 4, 5]
 
nums.map { $0 * 2 }               // [2, 4, 6, 8, 10]
nums.filter { $0.isMultiple(of: 2) } // [2, 4]
nums.reduce(0, +)                 // 15
 
let strings = ["1", "two", "3", "four"]
strings.compactMap { Int($0) }    // [1, 3]
 
let nested = [[1, 2], [3, 4]]
nested.flatMap { $0 }             // [1, 2, 3, 4]
 
// reduce(into:) for building a dictionary
let freq = nums.reduce(into: [:]) { dict, n in dict[n, default: 0] += 1 }

Sorting and ordering

MethodMutatesReturnsNotes
sorted()No[Element]Ascending order; requires Comparable.
sorted(by:)No[Element]Custom comparator.
sort()YesVoidIn-place; Array only.
sort(by:)YesVoidIn-place with comparator.
reversed()NoReversedCollectionO(1) lazy view; use Array(arr.reversed()) for a concrete copy.
shuffled()No[Element]Returns a new randomly ordered array.
let words = ["banana", "apple", "cherry"]
words.sorted()                          // ["apple", "banana", "cherry"]
words.sorted { $0.count < $1.count }    // ["apple", "banana", "cherry"]
 
// Sort by multiple criteria
users.sorted {
    $0.lastName == $1.lastName
        ? $0.firstName < $1.firstName
        : $0.lastName < $1.lastName
}

Search and existence

MethodReturnsNotes
contains(_:)BoolRequires Equatable; linear scan.
contains(where:)BoolPredicate-based; short-circuits.
firstElement?First element; nil for empty.
first(where:)Element?First matching element or nil.
lastElement?Last element; nil for empty.
last(where:)Element?Last matching element or nil.
firstIndex(of:)Index?Index of first occurrence; requires Equatable.
firstIndex(where:)Index?Index of first predicate match.
min() / max()Element?Minimum/maximum; requires Comparable.
min(by:) / max(by:)Element?Custom comparator.
let users = [User(name: "Alice", age: 30), User(name: "Bob", age: 25)]
 
users.contains { $0.age > 28 }          // true
users.first { $0.name == "Bob" }        // Optional(Bob)
users.max(by: { $0.age < $1.age })      // Optional(Alice)
users.firstIndex { $0.name == "Alice" } // Optional(0)

Iteration and enumeration

MethodReturnsNotes
forEach(_:)VoidIterate for side effects; cannot break or return from the loop.
enumerated()EnumeratedSequence(offset, element) pairs; use in for...in.
zip(_:_:)Zip2SequencePair elements from two sequences; stops at the shorter.
joined()FlattenSequenceFlatten a sequence of sequences; no closure needed.
joined(separator:)StringConcatenate string elements with a separator.
for (i, user) in users.enumerated() {
    print("\(i): \(user.name)")
}
 
let a = [1, 2, 3]
let b = ["one", "two", "three"]
for (n, s) in zip(a, b) {
    print(n, s)
}
// 1 one, 2 two, 3 three
 
["Hello", "world"].joined(separator: " ") // "Hello world"

Dictionary-specific methods

Dictionary conforms to Sequence over (key, value) pairs.

OperationSyntaxNotes
Filter by key or valuedict.filter { $0.key.hasPrefix("a") }Returns [K: V].
Map values onlydict.mapValues { $0 * 2 }Preserves keys; returns [K: V].
Compact-map valuesdict.compactMapValues { Int($0) }Drops nil values.
Group by keyDictionary(grouping: arr) { $0.key }Returns [K: [V]].
Mergedict.merging(other) { _, new in new }Second closure resolves key conflicts.
let scores = ["Alice": 90, "Bob": 72, "Carol": 88]
 
scores.filter { $0.value >= 80 }         // ["Alice": 90, "Carol": 88]
scores.mapValues { $0 + 10 }             // ["Alice": 100, "Bob": 82, "Carol": 98]
 
let grouped = Dictionary(grouping: users, by: { $0.department })

Lazy evaluation

Prefix a chain with .lazy to avoid intermediate arrays.

// Without lazy: creates three intermediate arrays
let result = (1...1_000_000)
    .map { $0 * $0 }
    .filter { $0.isMultiple(of: 3) }
    .prefix(5)
 
// With lazy: no intermediate arrays; elements computed on demand
let lazyResult = (1...1_000_000).lazy
    .map { $0 * $0 }
    .filter { $0.isMultiple(of: 3) }
    .prefix(5)
 
Array(lazyResult) // materialize when needed

Use .lazy when the sequence is large and you only consume a small prefix, or when combining many transformations.

Common gotchas

  • forEach cannot break out of iteration. Use a for...in loop when early exit is needed.
  • flatMap on a collection of optionals was renamed to compactMap in Swift 4.1. flatMap on a collection of sequences still flattens one level.
  • reduce with a mutable container argument copies the accumulator on each step. Use reduce(into:) for Array, Dictionary, and Set accumulators; it passes the accumulator inout.
  • reversed() returns a lazy ReversedCollection view, not a new Array. Passing it to a function expecting [Element] requires Array(collection.reversed()).
  • zip stops at the shorter sequence without error. If you expect equal lengths, assert before zipping to catch mismatches.
  • sorted() on a large array is O(n log n). For repeated lookups, consider a sorted data structure instead of re-sorting.
  • Set has contains in O(1). Array.contains is O(n). Convert to Set before repeated membership tests.