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:
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]
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:
Cons:
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:
Cons: