skip to content

What does `partition` return, and how is it different from filtering twice or from `groupBy`?

level: juniorimportance: should knowfreq 60%

answer

  1. Returns Pair<List<T>, List<T>>: (true-list, false-list)
  2. Single pass, single predicate evaluation
  3. Destructure: val (yes, no) = ...partition { }
  4. Both lists always returned, may be empty
  5. groupBy{bool} gives a Map and may omit a key

basics

~20 s

partition splits a collection into two lists based on a true/false test: a Pair where the first list holds items that match and the second holds those that don't. It does it in one pass.

solid answer

~40 s

`partition(predicate: (T) -> Boolean): Pair<List<T>, List<T>>` splits a collection by a boolean test in a **single pass**. `pair.first` holds elements where the predicate is `true`; `pair.second` holds the rest. It's preferable to calling `filter { p(it) }` and `filterNot { p(it) }` separately, which iterate twice and evaluate the predicate twice. You typically destructure it: `val (matches, rest) = items.partition { it.isActive }`. It's eager and returns concrete `List`s. Unlike `groupBy { p(it) }`, which yields a `Map<Boolean, List<T>>` (and may omit a key entirely if no element falls into it), `partition` always returns exactly two lists, either of which may be empty.

go deeper

for a junior

Knows partition splits into a Pair of two lists by a boolean and can destructure it.

for a middle

Explains the single-pass / single-evaluation advantage over filter + filterNot.

for a senior

Contrasts with groupBy{bool} (Map, possibly-missing key) and notes side-effect correctness implications.

for a principal

Considers readability and performance at scale and picks partition vs groupBy vs a manual fold for multi-way splits.

## What `partition` does `partition(predicate: (T) -> Boolean): Pair<List<T>, List<T>>` divides a collection into two lists using a boolean predicate, in **one iteration**. - `result.first` → elements for which the predicate returned `true`. - `result.second` → elements for which it returned `false`. ## Idiomatic use: destructuring ```kotlin val numbers = listOf(1, 2, 3, 4, 5, 6) val (even, odd) = numbers.partition { it % 2 == 0 } // even = [2, 4, 6], odd = [1, 3, 5] ``` Kotlin's destructuring relies on `Pair`'s `component1()`/`component2()`. ## vs. filtering twice ```kotlin val even = numbers.filter { it % 2 == 0 } val odd = numbers.filterNot { it % 2 == 0 } ``` This traverses the list **twice** and runs the predicate **twice per element**. `partition` does both in a single pass — clearer and cheaper. (If the predicate has side effects, double-evaluation could even be incorrect.) ## vs. `groupBy` `numbers.groupBy { it % 2 == 0 }` returns `Map<Boolean, List<T>>`. Differences: - Return type is a `Map`, not a `Pair` — you index by `true`/`false`. - If **no** element satisfies (or fails) the predicate, that key is **absent** from the map, so a lookup yields `null`. `partition` always returns two lists; the irrelevant one is simply **empty**. ## Properties - Eager; preserves source order within each list. - Available on `Iterable`, `Array`, and `Sequence`.

  • Which element ends up in `pair.first`?
    The ones for which the predicate returned true. pair.second holds the elements where it returned false.
  • Why is partition better than filter + filterNot for an expensive or side-effecting predicate?
    partition evaluates the predicate once per element in one pass; filter+filterNot evaluates it twice, doubling cost and risking incorrect results if the predicate has side effects.

Like a coin sorter with two trays: matches drop left, the rest drop right — in one pass of the coins.

saying these in an interview costs you the question

  • Saying partition returns a Map<Boolean, List<T>>
  • Putting true-results in second instead of first
  • Claiming it iterates twice
  • Not knowing it can be destructured

context