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.
| Method | Input | Returns | Notes |
|---|---|---|---|
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) -> Result | Result | Fold to a single value; always provide an initial value. |
reduce(into:_:) | (inout Result, Element) | Result | More 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
| Method | Mutates | Returns | Notes |
|---|---|---|---|
sorted() | No | [Element] | Ascending order; requires Comparable. |
sorted(by:) | No | [Element] | Custom comparator. |
sort() | Yes | Void | In-place; Array only. |
sort(by:) | Yes | Void | In-place with comparator. |
reversed() | No | ReversedCollection | O(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
| Method | Returns | Notes |
|---|---|---|
contains(_:) | Bool | Requires Equatable; linear scan. |
contains(where:) | Bool | Predicate-based; short-circuits. |
first | Element? | First element; nil for empty. |
first(where:) | Element? | First matching element or nil. |
last | Element? | 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
| Method | Returns | Notes |
|---|---|---|
forEach(_:) | Void | Iterate for side effects; cannot break or return from the loop. |
enumerated() | EnumeratedSequence | (offset, element) pairs; use in for...in. |
zip(_:_:) | Zip2Sequence | Pair elements from two sequences; stops at the shorter. |
joined() | FlattenSequence | Flatten a sequence of sequences; no closure needed. |
joined(separator:) | String | Concatenate 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.
| Operation | Syntax | Notes |
|---|---|---|
| Filter by key or value | dict.filter { $0.key.hasPrefix("a") } | Returns [K: V]. |
| Map values only | dict.mapValues { $0 * 2 } | Preserves keys; returns [K: V]. |
| Compact-map values | dict.compactMapValues { Int($0) } | Drops nil values. |
| Group by key | Dictionary(grouping: arr) { $0.key } | Returns [K: [V]]. |
| Merge | dict.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 neededUse .lazy when the sequence is large and you only consume a small prefix, or when combining many transformations.
Common gotchas
forEachcannotbreakout of iteration. Use afor...inloop when early exit is needed.flatMapon a collection of optionals was renamed tocompactMapin Swift 4.1.flatMapon a collection of sequences still flattens one level.reducewith a mutable container argument copies the accumulator on each step. Usereduce(into:)forArray,Dictionary, andSetaccumulators; it passes the accumulatorinout.reversed()returns a lazyReversedCollectionview, not a newArray. Passing it to a function expecting[Element]requiresArray(collection.reversed()).zipstops 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.Sethascontainsin O(1).Array.containsis O(n). Convert toSetbefore repeated membership tests.