Swift: Sum an array of integers using reduce
Video: Swift: Sum an array of integers using reduce by Taught by Celeste AI - AI Coding Coach
Swift with Copilot: Sum an Array with reduce
arr.reduce(0, +)— sum.arr.reduce(1, *)— product.arr.reduce("") { $0 + $1.description }— concat.reduce(initial, combine)is the universal "fold a sequence into a single value."
After map and filter, reduce is the third pillar of functional array operations.
The Copilot prompt
// Sum an array of numbers using reduce
let numbers = [1, 2, 3, 4, 5]
Copilot completes:
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0, +)
print(sum)
// 15
Walkthrough
reduce(initial, combine) walks the array, accumulating with the closure:
[1, 2, 3].reduce(0, +)
// step 1: 0 + 1 = 1
// step 2: 1 + 2 = 3
// step 3: 3 + 3 = 6
// final: 6
The closure takes (accumulator, element) and returns the new accumulator.
+ is a function (Int, Int) -> Int — passing it directly works. Same as:
numbers.reduce(0) { acc, x in acc + x }
numbers.reduce(0) { $0 + $1 }
Three forms, identical behavior.
Easier: just sum()?
Swift doesn't have a sum() method (a quirk). For numbers:
[1, 2, 3, 4, 5].reduce(0, +)
Or define your own:
extension Sequence where Element: Numeric {
func sum() -> Element {
reduce(0, +)
}
}
[1, 2, 3, 4, 5].sum() // 15
[1.5, 2.5, 3.0].sum() // 7.0
Now any numeric sequence has .sum(). Worth adding to your toolbox.
Other reductions
let nums = [1, 2, 3, 4, 5]
nums.reduce(1, *) // 120 — product
nums.reduce(0, max) // 5 — max (using max function)
nums.reduce(Int.max, min) // 1 — min
nums.reduce(0) { acc, x in acc + x * x } // 55 — sum of squares
For max/min specifically, the standard library has max() and min() directly:
nums.max() // Optional<Int> — 5
nums.min() // Optional<Int> — 1
Both return nil for empty sequences (hence the optional). reduce(0, max) returns 0 for empty — different semantics.
reduce(into:)
For accumulating into a mutable value (avoiding copies), use reduce(into:_:):
let words = ["hello", "world", "swift"]
let counts = words.reduce(into: [String: Int]()) { dict, word in
dict[word, default: 0] += 1
}
The closure receives inout accumulator — modify in place. Faster than reduce for dictionaries, sets, or large arrays.
// Word histogram
let text = "the quick brown fox jumps over the lazy dog the cat"
let histogram = text.split(separator: " ").reduce(into: [String: Int]()) {
$0[String($1), default: 0] += 1
}
// ["the": 3, "quick": 1, "brown": 1, ...]
For large data, reduce(into:) significantly outperforms reduce.
Concatenating strings
let parts = ["hello", "world", "swift"]
let joined = parts.reduce("") { $0 + $1 }
// "helloworldswift"
// With separator (use joined() for this; reduce is overkill)
let withSep = parts.joined(separator: " ")
// "hello world swift"
For string joining, use joined(separator:) — clearer and faster than reduce.
Building a structure
struct Stats {
var sum = 0
var max = Int.min
var min = Int.max
var count = 0
}
let stats = [3, 1, 4, 1, 5, 9, 2, 6].reduce(into: Stats()) { s, n in
s.sum += n
s.max = Swift.max(s.max, n)
s.min = Swift.min(s.min, n)
s.count += 1
}
let avg = Double(stats.sum) / Double(stats.count)
print(stats, avg)
One pass, multiple aggregates. reduce(into:) keeps the struct mutable inside the closure.
reduce vs forEach vs for-in
// reduce — one return value
let sum = arr.reduce(0, +)
// forEach — side effects, no return
arr.forEach { print($0) }
// for-in — most flexible, can break/continue
for x in arr where x > 0 {
if x > 100 { break }
process(x)
}
For accumulating: reduce. For side effects: forEach (or for-in). For loops with control flow: for-in.
Lazy reduction
reduce is eager — walks the whole sequence. For early termination, use first(where:), contains(where:), or a manual for-in with break.
// First even
arr.first(where: { $0 % 2 == 0 })
// Any negative
arr.contains(where: { $0 < 0 })
These are short-circuit — stop at the first match.
Performance
reduce(0, +)for numbers: as fast as a manualforloop.reduce(into:)for collections: no copy on each step, much faster.reduce("") { $0 + $1 }: O(n²) due to string copy on each step. Usejoined()instead.
For sums of huge arrays, the standard library's reduce is fine. For string joining, never reduce.
Common stumbles
Wrong initial value. [].reduce(0, max) returns 0, not nil. Make sure the initial value matches your semantics (0 for sum, 1 for product, Int.max for min).
reduce on empty array. Always returns initial. [].reduce(0, +) == 0. To distinguish empty from "all-zero," check .isEmpty first.
reduce for things that should be joined(). O(n²) string concat. Don't.
reduce for short-circuit. Walks the whole array. Use first(where:)/contains(where:).
Type inference fail. reduce("") { $0 + $1 } on [Int] errors — Int + String. Be explicit: reduce("") { $0 + String($1) }.
reduce(into:) not used for collections. Slow reduce makes a copy of the whole accumulator each step. For dicts/arrays, always reduce(into:).
What's next
Episode 13: Function vs Closure. Two ways to write a callable.
Recap
reduce(initial, combine) — fold a sequence into one value. reduce(0, +) for sum, reduce(1, *) for product, reduce(into: [...]()) for collection accumulation. For min/max, prefer arr.max() / arr.min() (returns optional). For string concatenation, use joined(separator:). For early exit, use first(where:) / contains(where:).
Next episode: function vs closure.