Skip to content
Folding and Scanning
step 1/7

Reading — step 1 of 7

Learn

~3 min readCollections in Depth

fold and scan are the two most powerful sequence operations. Programming in Scala devotes a chapter to higher-order operations, and these are the heavyweights.

foldLeft — left-to-right reduction

val nums = List(1, 2, 3, 4, 5)

val sum = nums.foldLeft(0)((acc, n) => acc + n)            // 15
val product = nums.foldLeft(1)((acc, n) => acc * n)         // 120
val max = nums.foldLeft(Int.MinValue)((acc, n) => acc max n)  // 5

foldLeft(initial)(combineFn):

  1. Start with initial
  2. For each element, combine acc and the element
  3. Return the final accumulator

Use this when you need a single result from a sequence.

foldRight — right-to-left

val nums = List(1, 2, 3, 4, 5)

nums.foldRight(0)((n, acc) => n + acc)        // also 15, but right-associated
nums.foldRight(List.empty[Int])((n, acc) => (n * 2) :: acc)   // List(2, 4, 6, 8, 10)

Note the parameter order is reversed: foldRight(initial)((element, acc) => ...). Useful for building up structures with prepend (cheap on List).

foldLeft is tail-recursive — preferred for large lists. foldRight isn't (without trampolining) — can stack-overflow on huge lists.

reduceLeft / reduceRight

Like fold but uses the FIRST element as the initial value:

val nums = List(1, 2, 3, 4, 5)
nums.reduceLeft(_ + _)        // 15
nums.reduceLeft(_ max _)      // 5

List.empty[Int].reduceLeft(_ + _)      // throws — no initial!

Use when there's no natural "empty" value AND you know the list is non-empty.

scanLeft — running totals

Like foldLeft, but returns ALL intermediate values:

val nums = List(1, 2, 3, 4, 5)
nums.scanLeft(0)(_ + _)
// List(0, 1, 3, 6, 10, 15)
//      ^               ^
//      initial         final
//
//      every step shown

Useful for cumulative sums, running averages, prefix arrays.

Common patterns

// Group by key
val users = List("Ada", "Bob", "Alice", "Brad")
users.foldLeft(Map.empty[Char, List[String]]) { (acc, name) =>
    val key = name.head
    acc.updated(key, name :: acc.getOrElse(key, Nil))
}

// Count occurrences
List(1, 1, 2, 3, 1, 2).foldLeft(Map.empty[Int, Int].withDefaultValue(0)) {
    (acc, n) => acc.updated(n, acc(n) + 1)
}
// Map(1 -> 3, 2 -> 2, 3 -> 1)

// Build a string
List("a", "b", "c").foldLeft("")((s, c) => s + c)        // "abc"

// Find max with index
List(3, 1, 4, 1, 5, 9, 2, 6).zipWithIndex.foldLeft((0, Int.MinValue)) {
    case ((bestIdx, bestVal), (n, i)) =>
        if (n > bestVal) (i, n) else (bestIdx, bestVal)
}

sum, product, max, min — shortcuts

For common cases, Scala provides direct methods:

List(1, 2, 3).sum         // 6
List(1, 2, 3).product     // 6
List(1, 2, 3).max         // 3
List(1, 2, 3).min         // 1

Built on fold internally. Use these when applicable; fold for custom logic.

Common mistakes

  • Wrong initial valuefoldLeft(0)(_ * _) gives 0 forever. For multiplication, use 1.
  • foldRight on huge lists — stack overflow. Convert to foldLeft + reverse, or use iterator.foldLeft.
  • Forgetting reduceLeft throws on empty — use reduceLeftOption for safety, or fall back to fold with explicit initial.
  • Mutating from inside fold — fold should be PURE. Mutation defeats the abstraction; use a regular loop instead.
  • Confusing scan with fold — fold returns one value; scan returns the sequence of all intermediate accumulators.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…