Reduce Into
ID |
swift.reduce_into |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Efficiency |
Language |
Swift |
Tags |
efficiency, idiomatic |
Description
Reports a .reduce(initial, { … .append(x) … }) chain that mutates
an accumulator captured by value. Swift offers reduce(into:), which
passes the accumulator as inout and avoids the copy-on-write copies
that the closure-by-value form triggers on every iteration.
xs.reduce([Int]()) { acc, x in // FLAW
var acc = acc
acc.append(x * 2)
return acc
}
xs.reduce(into: [Int]()) { acc, x in // OK
acc.append(x * 2)
}
Rationale
Sequence.reduce(::) takes the accumulator by value. When the
accumulator is a collection (Array, Set, Dictionary, …), every
iteration starts by making a fresh mutable copy and ends by returning
the new value, so the cost of the loop is O(n * size-of-accumulator).
Sequence.reduce(into:_:) passes the accumulator as inout, so the
storage is reused in place and the body of the closure can mutate it
directly. The asymptotic cost drops back to O(n).
The rule fires only when the second reduce argument is a closure
whose body contains a .append(…) or .insert(…) call — that
heuristic catches the common collection-building pattern without
flagging arithmetic reductions like .reduce(0, +).
Remediation
let doubled = xs.reduce(into: [Int]()) { acc, x in
acc.append(x * 2)
}
let counts = words.reduce(into: [String: Int]()) { acc, w in
acc[w, default: 0] += 1
}