skip to content

What does the standard-library function `groupBy` do, and what is the exact type of the value it returns?

level: juniorimportance: must knowfreq 80%

answer

  1. Returns Map<K, List<T>>
  2. Backed by LinkedHashMap — order preserved
  3. Two-arg overload adds valueTransform
  4. Eager, even on Sequence
  5. Counts? use groupingBy().eachCount()

basics

~10 s

groupBy sorts items into buckets using a key you choose. It returns a map where each key points to a list of all the items that produced that key.

solid answer

~30 s

`groupBy(keySelector)` walks the collection and builds a `Map<K, List<T>>`: for every element it computes a key with the lambda, and appends the element to the list stored under that key. Insertion order of both keys and per-key elements is preserved (it is backed by a `LinkedHashMap`). The two-argument overload `groupBy(keySelector, valueTransform)` returns `Map<K, List<V>>`, transforming each element before storing it. It is eager and runs immediately on `Iterable`/`Array`/`Sequence`. Example: `words.groupBy { it.first() }` yields `Map<Char, List<String>>`. For just counting use `groupingBy().eachCount()` instead, which avoids materializing the lists.

code

kotlin · 3 lines
kotlin
val nums = listOf(1, 2, 3, 4, 5, 6)
val parity = nums.groupBy { if (it % 2 == 0) "even" else "odd" }
// {odd=[1, 3, 5], even=[2, 4, 6]}

go deeper

for a junior

Knows groupBy buckets elements into a Map<K, List<T>> by a derived key.

for a middle

Mentions the valueTransform overload and that order is preserved via LinkedHashMap.

for a senior

Notes it is eager even on sequences and contrasts with groupingBy().eachCount() for count-only needs.

for a principal

Reasons about memory cost of materializing per-key lists vs streaming counts, and picks the right tool for large datasets.

## What `groupBy` is `groupBy` is an extension on `Iterable<T>`, `Array<T>`, and `Sequence<T>` that **restructures** a flat collection into a `Map` keyed by a value you derive from each element. ## Signatures - `groupBy(keySelector: (T) -> K): Map<K, List<T>>` - `groupBy(keySelector: (T) -> K, valueTransform: (T) -> V): Map<K, List<V>>` The single-argument form keeps the original elements; the two-argument form stores a transformed value instead. ## Semantics - For each element, the `keySelector` lambda computes a key. - The element (or its transform) is **appended** to the list under that key. - The backing map is a `LinkedHashMap`, so **key insertion order** and the **order of elements within each list** are preserved. - It is **eager**: even on a `Sequence`, calling `groupBy` consumes it and returns a fully built map. ## Example ```kotlin val words = listOf("apple", "avocado", "banana", "cherry") val byFirst: Map<Char, List<String>> = words.groupBy { it.first() } // {a=[apple, avocado], b=[banana], c=[cherry]} val lengths: Map<Char, List<Int>> = words.groupBy({ it.first() }, { it.length }) // {a=[5, 7], b=[6], c=[6]} ``` ## When to reach for something else - If you only need **counts**, prefer `groupingBy { ... }.eachCount()` — it returns `Map<K, Int>` without building intermediate lists. - If each key maps to exactly **one** value (not a list), use `associateBy` / `associate` instead.

  • If two elements produce the same key, what happens?
    Both are kept — they are appended to the same list under that key. No element is lost, unlike associateBy which keeps only the last.
  • Is the returned map mutable?
    The declared return type is the read-only Map, though the implementation is a LinkedHashMap; you should treat it as read-only.

Like sorting incoming mail into labeled pigeonholes — each slot ends up holding a stack of letters.

saying these in an interview costs you the question

  • Saying groupBy returns Map<K, T> (single value) instead of Map<K, List<T>>
  • Claiming groupBy is lazy on a Sequence
  • Confusing groupBy with associateBy (which drops duplicates)
  • Thinking order is randomized like a HashMap

context