Part of Swift with Copilot

Swift: Sum an array of integers using reduce

Sandy LaneSandy Lane

Video: Swift: Sum an array of integers using reduce by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

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 manual for loop.
  • reduce(into:) for collections: no copy on each step, much faster.
  • reduce("") { $0 + $1 }: O(n²) due to string copy on each step. Use joined() 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.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.