skip to content

What is the difference between onEach and forEach on a Sequence, and when would each pull from a real-world standpoint?

level: middleimportance: nice to knowfreq 30%

answer

  1. onEach = intermediate, returns same Sequence, lazy peek
  2. forEach = terminal, returns Unit, ends chain
  3. onEach without a terminal does nothing
  4. Use onEach to log mid-pipeline
  5. map transforms; onEach only peeks

basics

~20 s

onEach is intermediate: it runs a side effect on each element but passes the element along, so the chain keeps going lazily. forEach is terminal: it runs a side effect and ends the chain, returning nothing.

solid answer

~40 s

`onEach` is an **intermediate** op: it applies a side-effecting lambda to each element as it passes and returns the **same Sequence**, so you can keep chaining and it stays lazy (the side effect only fires when a terminal eventually runs). `forEach` is a **terminal** op: it drives iteration, applies the lambda for its side effect, and returns `Unit` — the chain ends there. So `onEach` is for *peeking* mid-pipeline (logging, debugging, incrementing a counter) without breaking the chain, and because it's intermediate, an `onEach` before a terminal that never runs will **not** execute. `forEach` is for the final consumption when you only want side effects and no result. A common gotcha: `seq.onEach { log(it) }` with no terminal logs nothing; you need a terminal (or use `forEach`).

code

kotlin · 6 lines
kotlin
val out = sequenceOf(1, 2, 3)
    .onEach { println("peek $it") } // intermediate, chainable
    .map { it * 10 }
    .toList()                        // terminal drives the peek

sequenceOf(1, 2, 3).forEach { println(it) } // terminal, returns Unit

go deeper

for a junior

Knows forEach loops over elements and ends the chain; may not know onEach exists.

for a middle

Correctly classifies onEach as intermediate and forEach as terminal and uses onEach for lazy logging.

for a senior

Explains the laziness consequence (onEach without a terminal is inert) and contrasts onEach vs map semantics.

for a principal

Sets team conventions for observability in lazy pipelines, avoiding hidden side effects in intermediate ops and choosing the right peek/consume primitive.

## onEach (intermediate) vs forEach (terminal) Both take a lambda `(T) -> Unit` and exist for **side effects**, but they sit on opposite sides of the intermediate/terminal divide. ### onEach — intermediate, lazy peek ```kotlin public fun <T> Sequence<T>.onEach(action: (T) -> Unit): Sequence<T> ``` - Returns the **same element stream** (`Sequence<T>`), so it's chainable. - The `action` runs **lazily**: only when a downstream terminal pulls each element. - Use it to observe a pipeline without changing it — logging, debug printing, metrics, or asserting invariants mid-chain. ```kotlin val result = sequenceOf(1, 2, 3) .onEach { println("saw $it") } // intermediate peek .map { it * 10 } .toList() // terminal triggers the peek // prints saw 1, saw 2, saw 3 interleaved with map; result [10,20,30] ``` Because `onEach` is intermediate, **with no terminal it does nothing**: ```kotlin sequenceOf(1, 2, 3).onEach { println(it) } // prints NOTHING ``` ### forEach — terminal, end of chain ```kotlin public fun <T> Sequence<T>.forEach(action: (T) -> Unit): Unit ``` - Returns `Unit`; you **cannot chain** after it. - It **drives** iteration immediately, pulling every element through the chain and running `action`. - Use it as the final step when you want side effects only and no collected result. ```kotlin sequenceOf(1, 2, 3) .map { it * 10 } .forEach { println(it) } // terminal: prints 10, 20, 30; returns Unit ``` ### Choosing between them - Need to **keep processing** after the side effect? -> `onEach` (then add a real terminal). - The side effect **is** the goal and nothing follows? -> `forEach`. - Debugging a lazy chain that 'does nothing'? Remember `onEach` won't fire without a terminal; temporarily swap to `forEach` or add `toList()`. ### Related distinction There is also `forEachIndexed` (terminal) and `onEachIndexed` (intermediate) following the same pattern, plus `map` (transforms and returns new values) vs `onEach` (returns the **same** values, side effect only). `map` is for producing data; `onEach` is for peeking at it.

  • Why does sequenceOf(1,2,3).onEach { println(it) } print nothing?
    onEach is an intermediate op; without a terminal op to pull elements, the lambda never runs.
  • How does onEach differ from map?
    Both are intermediate, but map returns transformed values of possibly a new type, while onEach runs a side effect and passes the original elements through unchanged.

saying these in an interview costs you the question

  • Calling forEach intermediate or chaining after it
  • Expecting onEach to run without a terminal
  • Confusing onEach (side effect, same values) with map (transform)
  • Thinking forEach returns a collection
  • Not realizing onEach keeps the chain lazy

context