Pocket Studio Academy
HomePart 11.12

Collection superpowers: filter, map, sumOf

Full course11 min read·4 questions

Kotlin can do in one line what a loop takes six lines to say — keep the items you want, change every item, and total them up. After this you will describe what you want instead of spelling out how to get it.

Six lines that should be one

Here is a job you already know how to do. Out of a list of prices, keep only the expensive ones.

Playground.ktkotlin
1val prices = listOf(3, 12, 7, 20, 5)
2val big = mutableListOf<Int>()
3for (p in prices) {
4  if (p >= 7) {
5    big.add(p)
6  }
7}
8println(big)

That works. It prints [12, 7, 20]. But look at how much of it is machinery: making an empty box, walking the list, adding to the box. Only one fragment — p >= 7 — says anything about the actual task.

Kotlin has a shorter way:

Playground.ktkotlin
val big = prices.filter { it >= 7 }

Same answer. One line. And the only thing left on that line is the interesting bit.

Think of it like this

Picture a production line in a jam factory.

The fruit rolls past a sieve — anything too small drops through and only the good ones carry on. Further along, a machine stamps a label on every jar that passes. At the end, a scale weighs the lot and prints one total.

Nobody stands at the belt saying "pick up jar, look at jar, put jar in crate, pick up next jar". You install the sieve, the stamper and the scale, and describe what each one does to one item. The belt handles the repetition.

filter is the sieve. map is the stamper. sumOf is the scale.

The brace is a tiny function

Playground.ktkotlin
prices.filter { it >= 7 }

Those braces hold a — a scrap of code with no name, handed to filter as an instruction. filter runs it once for every item.

Inside, is the item currently being looked at. Kotlin supplies that name automatically whenever the lambda takes exactly one input, so you do not have to invent one. Lesson 1.17 pulls lambdas apart properly; for now, read { it >= 7 } as "is this one seven or more?".

A lambda that answers true or false is called a . Every function on this page that asks a yes/no question takes one.

The three you will use every day

filter — keep the ones that pass

Playground.ktkotlin
1val names = listOf("Ada", "Al", "Grace")
2println(names.filter { it.length > 2 })

Prints [Ada, Grace]. hands back a new list containing only the items whose was true. filterNot keeps the opposite ones.

map — change every item

Playground.ktkotlin
println(prices.map { it * 2 })

Prints [6, 24, 14, 40, 10]. runs your lambda on each item and collects the results. The new list is always the same length as the old one — every item goes in, every item comes out changed.

It does not have to come out as the same , either:

Playground.ktkotlin
println(names.map { it.length })

Strings go in, numbers come out: [3, 2, 5].

Two different things are called map

The from Lesson 1.11 is a collection of key-and-value pairs. The map on this page is a function that transforms every item.

They are unrelated, and Kotlin genuinely uses one word for both. Tell them apart by where the word sits: mapOf(...) builds a collection, list.map { ... } transforms one.

sumOf — total up one number per item

Playground.ktkotlin
println(names.sumOf { it.length })

Prints 10, because 3 + 2 + 5 is ten. asks your lambda for a number from each item and adds them all together. When the items are already numbers, plain sum() does the same job with no lambda at all.

Eight more worth knowing now

Written asHands back
list.count { it > 5 }How many items pass
list.any { it > 5 }true if at least one passes
list.all { it > 5 }true only if every one passes
list.none { it > 5 }true if not a single one passes
list.maxOrNull()The biggest item, or null if the list is empty
list.sortedBy { it.length }A new list in order of whatever you name
list.firstOrNull { it > 5 }The first passing item, or null
list.joinToString(" & ")One string with the items glued together

Notice how many of those end in OrNull. An empty list has no biggest item, so those functions honestly say null rather than guessing. Lesson 1.13 is about handling that answer.

Chaining: the belt keeps going

Because each of these hands back a new , you can bolt the next one straight on to the end. That is :

Playground.ktkotlin
1val total = prices
2  .filter { it >= 7 }
3  .map { it * 2 }
4  .sum()
5println(total)

Read it top to bottom, like a sentence: take the prices, keep the ones from seven up, double each of them, add them together. It prints 78, because 12 + 7 + 20 is 39, and doubled that is 78.

Tip

None of these functions ever change the list you started with. prices still holds all five prices after every line above. Values you cannot accidentally alter are called , and they are one of the reasons Kotlin code tends to stay correct as it grows.

Try it in Pocket Studio
  1. Open Pocket Studio, tap Projects, and open your Kotlin practice project.
  2. Tap Editor and open Playground.kt.
  3. Select everything in the file and delete it.
  4. Type the eleven-line program from the walkthrough above.
  5. Tap Run and read the Output panel.

You should see exactly this:

text
1[12, 7, 20]
2[6, 24, 14, 40, 10]
339
4[Ada, Grace]
510
6Ada & Al & Grace
  1. Now add one more line just before the closing brace: println(prices).
  2. Run again. All five original prices are still there, in their original order — proof that filter and map build new lists rather than editing the old one.
Error Doctor5 common errors
e: Type mismatch: inferred type is Int but Boolean was expected
Meansfilter needs a yes/no answer from your lambda, and yours handed back a number. Usually you wrote { it * 2 } where you meant a comparison.
FixMake the lambda ask a question: { it > 2 }. If you actually wanted to change each item rather than choose between them, use map instead of filter.
e: Unresolved reference: it
Meansit only exists when you have not named the lambda's input. As soon as you write { n -> ... }, the name is n and it stops existing.
FixPick one style: either { it > 7 } or { n -> n > 7 }. Do not mix them in the same braces.
e: Unresolved reference: sumBy
MeanssumBy was an older name that has since been removed from Kotlin. Tutorials written before 2021 still use it.
FixUse sumOf instead — same idea, and it copes with decimals as well as whole numbers.
e: Not enough information to infer type variable T
MeansYou wrote listOf() with nothing inside, so Kotlin has no way to know what kind of list you meant, and it refuses to guess.
FixSay what it holds: listOf<Int>(), or mutableListOf<String>(). Once there is at least one item in the brackets, Kotlin works it out by itself.
e: Operator call corresponds to a dot-qualified call 'prices.maxOrNull().plus(1)' which is not allowed on a nullable receiver
MeansmaxOrNull hands back Int? — a number or null, because an empty list has no biggest item. You tried to add one to that without dealing with the null case.
FixSupply a fallback first: (prices.maxOrNull() ?: 0) + 1. Lesson 1.13 explains exactly what Kotlin is protecting you from here.
Recap
  • keeps the items that pass a ; the result is a new, possibly shorter list.
  • runs a lambda on every item and collects the results; same length, possibly a different .
  • turns each item into a number and totals them.
  • is the automatic name for the current item when the lambda takes one input.
  • None of these change the original — they always build a new one, which is what makes safe.
  • Next: the most important safety idea in Kotlin. Why null exists, what a NullPointerException really is, and the two characters that make it a non-issue.