skip to content

What does zip do on two Kotlin collections, and what determines the size of the result?

level: juniorimportance: must knowfreq 70%

answer

  1. Pairs by index, position-by-position
  2. Stops at the shorter -> size = min
  3. Transform overload avoids Pair
  4. Infix: a zip b
  5. unzip() reverses it

basics

~10 s

zip pairs up elements from two lists by position: first with first, second with second, and so on. The result stops at the shorter list, so its length equals the smaller of the two.

solid answer

~30 s

list.zip(other) returns a List<Pair<A, B>> pairing elements by index. It is truncating: the result length is minOf(list.size, other.size); leftover elements in the longer collection are dropped. An overload zip(other) { a, b -> ... } takes a transform lambda and returns List<R> directly, avoiding intermediate Pair allocation. The infix form a zip b works too. zip is also available on Sequence (lazy) and on arrays. Pairs are accessed via .first/.second, or destructured: for ((x, y) in a.zip(b)). To go the other way, unzip() splits a List<Pair<A, B>> back into Pair<List<A>, List<B>>.

code

kotlin · 4 lines
kotlin
val ids = listOf(1, 2, 3, 4)
val labels = listOf("a", "b")
println(ids.zip(labels))                 // [(1, a), (2, b)]
println(ids.zip(ids) { x, y -> x + y })  // [2, 4, 6, 8]

go deeper

for a junior

States that zip pairs by index and stops at the shorter list.

for a middle

Adds the transform overload, infix form, and unzip as the inverse.

for a senior

Notes lazy zip on Sequence, no padding variant, and minimizing allocation via the lambda overload.

for a principal

Frames zip in API-design terms (truncation as a deliberate total-function choice) and when to prefer manual padding or Sequence for large/infinite inputs.

## What zip does `zip` combines two collections **element by element by position**. Element 0 of the first pairs with element 0 of the second, element 1 with element 1, etc. The result is a `List<Pair<A, B>>`. ```kotlin val names = listOf("Ann", "Bob", "Cy") val ages = listOf(30, 25, 40) val people = names.zip(ages) // [(Ann, 30), (Bob, 25), (Cy, 40)] ``` ## Truncation rule `zip` is **truncating**: it stops at the **shorter** collection. The result size is `minOf(a.size, b.size)`; surplus elements in the longer one are silently dropped. ```kotlin listOf(1, 2, 3, 4).zip(listOf("a", "b")) // [(1, a), (2, b)] -> size 2 ``` There is **no built-in "zip with padding"** in the standard library; if you need that you pad manually. ## The transform overload To avoid building `Pair` objects you can pass a lambda: ```kotlin val sums = listOf(1, 2, 3).zip(listOf(10, 20, 30)) { x, y -> x + y } // [11, 22, 33] -> List<Int>, no Pair allocated ``` ## Forms and access - Infix: `a zip b`. - A `Pair` exposes `.first` and `.second`, and supports **destructuring**: `for ((name, age) in people) { ... }`. - `zip` exists on `Iterable`, `Sequence` (lazy), `Array`, and `CharSequence`. ## Inverse: unzip `unzip()` turns a `List<Pair<A, B>>` back into `Pair<List<A>, List<B>>`: ```kotlin val (ns, ag) = people.unzip() // ([Ann, Bob, Cy], [30, 25, 40]) ```

  • If one list has 5 elements and the other has 3, how many pairs result?
    Three. zip truncates to the shorter collection, so the result size is min(5, 3) = 3.
  • How do you sum two parallel lists element-wise without creating Pair objects?
    Use the transform overload: a.zip(b) { x, y -> x + y }, which returns List<Int> directly.

Like a clothing zipper: teeth on each side mesh one-to-one, and the zip stops where the shorter side runs out.

saying these in an interview costs you the question

  • Claiming zip pads the shorter list with null or default values
  • Saying the result length is the larger list's size
  • Thinking zip matches by value rather than by position/index
  • Believing zip mutates the original collections
  • Not knowing about the transform-lambda overload

context