Total the character lengths of a list of names, returning 0 for an empty list
totalLength must return the combined character count of a list of names. The current implementation crashes when the list is empty.
Constraints: an empty list must return 0; do not add an isEmpty() guard or an explicit loop; do not build an intermediate list of lengths.
fun totalLength(names: List<String>): Int =
names.map { it.length }.reduce { acc, len -> acc + len }
Rewrite the body as a single stdlib call, and say why reduce cannot do this job.
reduce has no seed: it starts from the first element, throws on an empty collection, and its accumulator has the element type. fold takes an initial value, so it handles the empty case and changes type: fold(0) { acc, n -> acc + n.length }.
- ✗Reaching for
reducewhen the result type differs from the element type - ✗Forgetting that
reducethrowsUnsupportedOperationExceptionon an empty collection - ✗Guarding with
isEmpty()instead of seeding the accumulator
- →When would
runningFoldbe a better fit here thanfold? - →How does
reduceOrNullchange the behaviour on an empty collection?
Why reduce crashes
reduce has no initial value: it takes the first element as the accumulator and carries on. On an empty collection there is nothing to take, so it throws UnsupportedOperationException. There is a second limit too: reduce's accumulator has the element type, and here we need an Int out of Strings.
The fold solution
fun totalLength(names: List<String>): Int =
names.fold(0) { acc, name -> acc + name.length }
The seed 0 fixes the accumulator type (Int) and is itself the result for an empty list. No intermediate list of lengths is built — the map is gone.
When reduce is the right call
val longest = names.reduce { a, b -> if (a.length >= b.length) a else b }
reduce fits when the result type equals the element type and the collection is guaranteed non-empty. Without that guarantee use reduceOrNull, which hands back null instead of throwing.