0Pricing
Kotlin Academy · Lesson

Flow Operators: map, filter, transform, and take

Apply intermediate operators to transform and limit Flow emissions.

Flow Operators: map, filter, transform, and take is a free Kotlin Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is a Flow?

A Flow is a cold, asynchronous stream. Intermediate operators like map, filter, and transform are lazy — they run only when collected.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun numbers(): Flow<Int> = flow {
    repeat(5) { emit(it + 1) }
}
fun main() = runBlocking {
    numbers().collect { println(it) }
}

map Operator

map transforms each emitted value. It is a 1-to-1 transformation applied lazily.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    (1..5).asFlow()
        .map { it * it }
        .collect { println(it) } // 1 4 9 16 25
}

filter Operator

filter passes only elements matching the predicate, dropping the rest.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    (1..10).asFlow()
        .filter { it % 2 == 0 }
        .collect { println(it) } // 2 4 6 8 10
}

transform Operator

transform is the most general operator: you can emit zero, one, or many values per input element.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    (1..3).asFlow()
        .transform { n ->
            emit("Item $n")
            emit("Done $n")
        }
        .collect { println(it) }
}

take Operator

take(n) takes only the first n elements and cancels the upstream flow after that.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flow {
        repeat(100) { emit(it) }
    }.take(3)
     .collect { println(it) } // 0 1 2
}

takeWhile and dropWhile

takeWhile takes elements while the predicate is true; dropWhile drops until the predicate becomes false.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    (1..10).asFlow()
        .takeWhile { it < 5 }
        .collect { println(it) } // 1 2 3 4

    (1..10).asFlow()
        .dropWhile { it < 5 }
        .collect { println(it) } // 5 6 7 8 9 10
}

Chaining Operators

Operators compose: each operator returns a new Flow, enabling declarative pipelines.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    (1..20).asFlow()
        .filter { it % 3 == 0 }
        .map { it * 2 }
        .take(4)
        .collect { println(it) } // 6 12 18 24
}

mapNotNull

mapNotNull maps and drops null results in one step — equivalent to map + filter { it != null }.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flowOf("1", "two", "3", "four", "5")
        .mapNotNull { it.toIntOrNull() }
        .collect { println(it) } // 1 3 5
}

flatMapConcat

flatMapConcat maps each element to a flow and concatenates the results sequentially.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    (1..3).asFlow()
        .flatMapConcat { n -> flowOf("$n-a", "$n-b") }
        .collect { println(it) }
}

onEach for Side Effects

onEach performs a side effect on each element without changing the stream — good for logging or analytics in a pipeline.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    (1..5).asFlow()
        .onEach { println("Processing: $it") }
        .filter { it % 2 == 0 }
        .collect { println("Collected: $it") }
}

Flow is Cold

Each call to collect restarts the flow from scratch. No element is emitted or transformed until a terminal operator triggers collection.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val flow = (1..3).asFlow().map { println("map $it"); it * 2 }
    println("Before collect")
    flow.collect { println("got $it") }
    println("Second collect:")
    flow.collect { println("got $it") }  // runs again
}

Quick Check

Which operator emits zero, one, or many values per upstream element?

Recap

map transforms 1-to-1; filter drops elements; transform emits any number; take limits count. Chain them declaratively — the flow is cold and only runs when collected.

Frequently asked questions

Is the “Flow Operators: map, filter, transform, and take” lesson free?

Yes — the full text of “Flow Operators: map, filter, transform, and take” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.

What will I learn in “Flow Operators: map, filter, transform, and take”?

Apply intermediate operators to transform and limit Flow emissions. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Kotlin Academy?

No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Flow Operators: map, filter, transform, and take” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Kotlin Academy lesson?

Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Flow Operators: map, filter, transform, and take
  2. catch and onCompletion: Error Handling in Flow
  3. combine and zip: Merging Multiple Flows
  4. flowOn and buffer: Context and Backpressure
← Back to Kotlin Academy