Skip to Content
Course content

87: zipWithNext and associateBy

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When you're processing collections in Kotlin, you'll often find yourself in a situation where an element doesn't exist in a vacuum—it only matters in relation to the one that came before it. I see this all the time in telemetry data or financial logs. For instance, if you have a list of stock prices, you aren't usually interested in the price at 10:00 AM alone; you want to know if it went up or down since 9:59 AM.

The fragile dance of index offsets

The "standard" way most people approach this—especially if they're coming from C++ or Java—is the index-based for loop. It looks something like this:

val prices = listOf(100.0, 102.0, 101.0, 105.0)
val diffs = mutableListOf<Double>()

for (i in 0 until prices.size - 1) {
    val current = prices[i]
    val next = prices[i + 1]
    diffs.add(next - current)
}

Now, this works, but it's brittle. You have to remember to use until prices.size - 1. If you accidentally use ..prices.size or forget the minus one, you're hitting an IndexOutOfBoundsException. It's a tiny detail, but it's exactly the kind of noise that clutters a pull request and leads to silly bugs. I find it distracting because the intent (comparing neighbors) is buried under the mechanics (managing an integer pointer).

This is where zipWithNext comes in. It abstracts away the index entirely by creating pairs of adjacent elements for you.

val diffs = prices.zipWithNext { current, next -> 
    next - current 
}

It's cleaner, and it's safe. If the list has fewer than two elements, zipWithNext simply returns an empty list rather than crashing. You've shifted from telling the computer how to walk through the array to telling it what you want to happen between every two items.

Cleaning up the map boilerplate

Once you've processed your data, you often need to reorganize it for fast lookup. Let's say we have a list of Trade objects, and we need to be able to grab a specific trade by its unique ID without iterating through the whole list every single time.

The naive approach is to initialize a mutable map and populate it manually:

val tradeMap = mutableMapOf<String, Trade>()
trades.forEach { trade -> 
    tradeMap[trade.id] = trade 
}

I'll be honest: there is nothing technically "wrong" with this. It's performant and clear. However, it's verbose. You're declaring a mutable variable, setting up a loop, and manually assigning keys. In a professional codebase, this is just boilerplate that doesn't add value.

Kotlin provides associateBy to handle this in a single, declarative expression. It takes a lambda that defines what the key should be, and it uses the object itself as the value.

val tradeMap = trades.associateBy { it.id }

It does exactly what the manual loop does, but it expresses the transformation as a single operation. If you have duplicate keys, the last one wins—which is the same behavior as the manual map assignment. By using associateBy, you're signaling to the next developer: "I am transforming this list into a lookup table," rather than "I am running a loop that happens to put things in a map."




📋 Practical Task

Build a Temperature Trend Mapper

You are working on a weather monitoring system. You have a list of Reading objects, each containing a sensorId and a temperature. Your goal is to calculate the temperature change between consecutive readings for a specific sensor and then create a lookup map of the original readings.

Requirements:

  • Create a data class Reading(val sensorId: String, val temperature: Double).
  • Given a list of readings: listOf(Reading("S1", 20.0), Reading("S1", 22.5), Reading("S1", 21.0), Reading("S1", 24.0)).
  • Use zipWithNext to create a list of the differences (the delta) between each consecutive temperature.
  • Use associateBy to create a map where the key is the index of the reading in the original list and the value is the Reading object itself.
  • Print both the list of deltas and the resulting map to the console.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.