ProgrammingMobile Developer

How does the functional reduce work in Swift, what are the advantages and features of using it? What are the mistakes and pitfalls when applying reduce to collections?

Pass interviews with Hintsage AI assistant

Answer.

The reduce mechanism relates to functional operations on data collections and was brought to Swift from functional languages (map-reduce, fold). Historically, this function allows turning any collection into a single aggregated value (for example, sum, product, concatenation of strings, etc.) by traversing all its elements and accumulating the result. The basic problem it solves is concise, readable, and bug-free data aggregation instead of manual loops.

In Swift, reduce is defined as a method of collections:

func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

This means you specify the initial value, and then write a function that returns a new aggregated value for each element and the current accumulator.

Code example:

let numbers = [1, 2, 3, 4] let sum = numbers.reduce(0) { $0 + $1 } // 10 let joined = numbers.reduce("") { $0 + String($1) } // "1234"

Key features:

  • Allows writing collection aggregation in a single line of code
  • Guarantees the absence of side effects — does not modify collections, works functionally
  • Can be used for any types (not just numbers), including those with throwing errors (throws)

Tricky questions.

How does reduce work on an empty collection?

Reduce is applied to each element of the collection. If the collection is empty, the initial value is returned, and there will be no closure calls.

Code example:

let empty: [Int] = [] let sum = empty.reduce(100) { $0 + $1 } // 100

Can reduce change the original collection?

No, reduce is a pure function; it does not change the original collection. All changes occur only with the accumulator.

What is the difference between reduce and reduce(into:)?

reduce(into:) allows mutating the accumulator on each pass and works more efficiently with value types, where creating a new copy (copy-on-write) is costly.

Code example:

let nums = [1, 2, 3] let squares = nums.reduce(into: []) { (result: inout [Int], item) in result.append(item * item) } // squares == [1, 4, 9]

Typical mistakes and anti-patterns

  • Incorrect choice of the initial value (for example, for multiplication aggregation — 0 instead of 1)
  • Side effects inside the reduce closure (for example, modifying external variables)
  • Using reduce for tasks that are better solved by other functions (for example, filter, map)

Real-life example

Negative case

A developer wanted to sum the prices of products stored in an array of products. They used reduce with an initial value of 0.0, but the closure was structured in such a way that it sometimes returned a negative sum when discounts were applied, leading to incorrect totals and issues with tax calculations.

Pros:

  • Concise code
  • No manual loops

Cons:

  • The error became apparent only in production
  • Incorrect business value of the final sum

Positive case

Used reduce(into:) to create a cache from an array of entities by id:

let objects: [Entity] = ... let cache = objects.reduce(into: [:]) { $0[$1.id] = $1 }

Pros:

  • Fast, efficient, no copying of the array
  • Clear typing of the cache

Cons:

  • Code reduce(into:) is slightly less readable for beginners
  • Can accidentally overwrite values with duplicate keys