Collections & Functional Operators
📚 Collections & Functional Operators
📋 List, Set, and Map
listOf, setOf, and mapOf all produce read-only collections by default — no add, remove, or index-assignment is available on them. That's a deliberate mirror of Chapter 1's val vs var distinction, applied to collections: read-only is the default, mutability is opt-in.
Mutable Versions
mutableListOf(), mutableSetOf(), and mutableMapOf() return collections that support add(), remove(), and similar. Reach for these only when a collection genuinely needs to change after creation.
to — Building Pairs
"Alice" to 30 creates a Pair("Alice", 30). It's an infix function, not special syntax — mapOf is really just a function that accepts a list of Pairs.
Collection Literals: JavaScript vs Kotlin
| JavaScript | Kotlin | |
|---|---|---|
| List/Array | const names = ["Alice", "Bob"]; | val names = listOf("Alice", "Bob") |
| Map/Object | const ages = {Alice: 30, Bob: 25}; | val ages = mapOf("Alice" to 30, "Bob" to 25) |
| Read-only by default? | No — arrays/objects are always mutable | Yes — listOf/mapOf are read-only |
🗺️ map — Transform Every Element
map applies the lambda to every element and returns a new list of the results, one-to-one with the input — the same shape as JavaScript's Array.prototype.map.
🔍 filter — Keep Some Elements
filter keeps only elements where the lambda returns true — Kotlin's equivalent of JavaScript's Array.prototype.filter, with the same predicate-based idea.
➕ reduce — Combine into One Value
reduce walks the list, accumulating a single result — acc starts as the first element, then each step combines it with the next. fold is nearly identical but takes an explicit initial value instead of using the first element, which also means fold works safely on an empty list (reduce throws on an empty collection, since it has no first element to start from).
🌊 flatMap — Map, Then Flatten
flatMap is map followed by flattening one level of nesting — useful whenever the transformation for each element produces its own list, and the goal is a single combined list rather than a list of lists.
Functional Operators Quick Reference
| Operator | Input → Output | JavaScript Equivalent |
|---|---|---|
| map | N elements → N transformed elements | Array.prototype.map |
| filter | N elements → subset matching a condition | Array.prototype.filter |
| reduce / fold | N elements → 1 accumulated value | Array.prototype.reduce |
| flatMap | N elements → N lists, flattened into 1 | Array.prototype.flatMap |
⚡ Sequences — Lazy Evaluation
Chaining several operators on a regular collection (List, Set) creates a full intermediate list at every step. A Sequence instead evaluates lazily, element by element, through the whole chain at once:
The equivalent chain without .asSequence() — (1..1_000_000).filter { ... }.map { ... }.take(5) — would first filter all one million numbers into a 500,000-element list, then map all of those, and only then take 5. On large or infinite ranges, that gap in work done is significant; on small lists (the vast majority of real code), plain operators are simpler and the difference doesn't matter.
💻 Coding Challenges
Challenge 1: List, Set, and Map Basics
Create a List<String> of five city names including one duplicate, convert it to a Set to remove the duplicate, and print both. Then create a Map<String, Int> of three cities to their population (in millions) and print the population of one city by key lookup.
Goal: Get comfortable creating and reading from all three core collection types.
Challenge 2: filter, map, and reduce Chained
Given val prices = listOf(12.50, 8.00, 25.00, 3.50, 40.00), filter to prices over 10.0, map each remaining price by applying a 20% discount, then use reduce to sum the discounted prices into a single total. Print the total.
Goal: Chain three functional operators together in one realistic calculation.
Challenge 3: flatMap
Given val orders = listOf(listOf("Apple", "Bread"), listOf("Milk"), listOf("Eggs", "Cheese", "Butter")) (each inner list is one customer's order), use flatMap to produce a single flat list of every item across all orders, then print how many total items were ordered using .size.
Goal: Practice flattening a list of lists into one combined list.
Kotlin code very rarely writes a manual for-loop to transform data — chains like list.filter { ... }.map { ... }.sortedBy { ... } are the idiomatic default. Each operator returns a new collection, so the whole chain reads top-to-bottom as a data pipeline: start with the source, keep applying steps, end with the result.
🎯 What's Next
Next chapter: Control Flow & Destructuring — when expressions, ranges, loops, and destructuring declarations.