C4G: Map, Filter und Reduce einzeln anwenden (Kotlin)
Ich kann
map(),filter()undreduce()einzeln verwenden, um Listen zu transformieren, zu filtern und zu aggregieren.
Lernziele
| # | Lernziel | Beantwortet in |
|---|---|---|
| 1 | Ich kann map() verwenden, um eine Transformation auf jedes Element einer Liste anzuwenden. | 1. Map anwenden |
| 2 | Ich kann filter() verwenden, um Elemente aus einer Liste nach einem Kriterium auszuwählen. | 2. Filter anwenden |
| 3 | Ich kann reduce() / fold() verwenden, um eine Liste auf einen einzelnen Wert zu reduzieren. | 3. Reduce anwenden |
Überblick
In Kotlin sind map, filter und fold/reduce direkt auf Collections verfügbar (kein Stream nötig).
| Operation | Was sie tut | Eingabe → Ausgabe |
|---|---|---|
map {} | Transformiert jedes Element | Liste → Liste (gleiche Anzahl) |
filter {} | Wählt Elemente nach Kriterium | Liste → Liste (gleiche oder weniger) |
fold() / reduce() | Fasst alle Elemente zusammen | Liste → Einzelwert |
1. Map anwenden
map { transformation } wendet eine Funktion auf jedes Element an und gibt eine neue Liste zurück.
// Rezeptzutaten für 4 statt 2 Personen skalieren
val portions = listOf(100, 250, 50, 200) // Gramm pro Zutat
val scaled = portions.map { it * 2 }
println(scaled) // [200, 500, 100, 400]
map verändert die Originalliste nicht:
println(portions) // [100, 250, 50, 200] — unverändert
Weitere Beispiele
// Dateinamen normalisieren
val filenames = listOf("Bericht.PDF", "foto.JPG", "notizen.TXT")
val normalized = filenames.map { it.lowercase() }
println(normalized) // [bericht.pdf, foto.jpg, notizen.txt]
// CHF zu EUR umrechnen (Kurs 0.94)
val chfPrices = listOf(9.90, 24.50, 149.00)
val eurPrices = chfPrices.map { "%.2f".format(it * 0.94) }
println(eurPrices) // [9.31, 23.03, 140.06]
// Spielernamen mit Rang versehen
val players = listOf("Alice", "Bob", "Charlie")
val ranked = players.mapIndexed { i, name -> "#${i + 1} $name" }
println(ranked) // [#1 Alice, #2 Bob, #3 Charlie]
Map mit Funktionsreferenz
fun maskEmail(email: String): String {
val (name, domain) = email.split("@")
return "${name.first()}***@$domain"
}
val emails = listOf("anna@bbw.ch", "max@gmail.com", "leo@bluewin.ch")
val masked = emails.map(::maskEmail)
println(masked) // [a***@bbw.ch, m***@gmail.com, l***@bluewin.ch]
2. Filter anwenden
filter { bedingung } behält nur Elemente, für die die Bedingung true ergibt.
// Nur Fehlermeldungen aus Logeinträgen filtern
val logs = listOf(
"INFO: Server started",
"ERROR: Connection refused",
"INFO: Request processed",
"ERROR: Timeout after 30s",
"WARN: Disk usage 85%",
)
val errors = logs.filter { it.startsWith("ERROR") }
println(errors)
// [ERROR: Connection refused, ERROR: Timeout after 30s]
Weitere Beispiele
// Nur volljährige Benutzer
val ages = mapOf("Anna" to 22, "Ben" to 16, "Clara" to 19, "David" to 14, "Eva" to 30)
val adults = ages.filter { (_, age) -> age >= 18 }
println(adults) // {Anna=22, Clara=19, Eva=30}
// Nur verfügbare Produkte (Bestand > 0)
data class Product(val name: String, val stock: Int)
val inventory = listOf(
Product("Laptop", 5), Product("Maus", 0),
Product("Tastatur", 12), Product("Monitor", 0), Product("Kabel", 34),
)
val available = inventory.filter { it.stock > 0 }
println(available.map { it.name }) // [Laptop, Tastatur, Kabel]
// Nur gültige Postleitzahlen (4-stellig, numerisch)
val codes = listOf("8000", "abc", "1234", "99", "3012", "")
val valid = codes.filter { it.length == 4 && it.all { c -> c.isDigit() } }
println(valid) // [8000, 1234, 3012]
Filter mit benannter Funktion
fun isStrongPassword(pw: String): Boolean =
pw.length >= 8
&& pw.any { it.isUpperCase() }
&& pw.any { it.isDigit() }
val passwords = listOf("abc", "Passw0rt", "12345678", "Sicher99!", "hi")
val strong = passwords.filter(::isStrongPassword)
println(strong) // [Passw0rt, Sicher99!]
3. Reduce anwenden
fold(startwert) { acc, element -> ... } kombiniert alle Elemente schrittweise zu einem Einzelwert.
// Warenkorb-Gesamtpreis berechnen
val cart = listOf(4.50, 12.90, 3.20, 8.00)
val total = cart.fold(0.0) { acc, price -> acc + price }
println(total) // 28.6
Wie Fold funktioniert (Schritt für Schritt)
Für listOf(4.50, 12.90, 3.20, 8.00).fold(0.0) { acc, price -> acc + price }:
| Schritt | acc | price | Ergebnis |
|---|---|---|---|
| Start | 0.0 | 0.0 | |
| 1 | 0.0 | 4.50 | 4.50 |
| 2 | 4.50 | 12.90 | 17.40 |
| 3 | 17.40 | 3.20 | 20.60 |
| 4 | 20.60 | 8.00 | 28.60 |
Weitere Beispiele
// Längsten String finden
val cities = listOf("Bern", "Zürich", "Winterthur", "Basel")
val longest = cities.reduce { acc, c -> if (c.length > acc.length) c else acc }
println(longest) // Winterthur
// Verschachtelte Listen flach machen
val nested = listOf(listOf(1, 2), listOf(3), listOf(4, 5, 6))
val flat = nested.fold(emptyList<Int>()) { acc, lst -> acc + lst }
println(flat) // [1, 2, 3, 4, 5, 6]
// Häufigkeiten zählen
val chars = "abracadabra".toList()
val freq = chars.fold(emptyMap<Char, Int>()) { acc, c ->
acc + (c to (acc.getOrDefault(c, 0) + 1))
}
println(freq) // {a=5, b=2, r=2, c=1, d=1}
Fold vs. Reduce
fold hat einen expliziten Startwert, reduce nimmt das erste Element:
// fold: sicher bei leeren Listen
val safeSum = emptyList<Int>().fold(0) { acc, x -> acc + x }
println(safeSum) // 0
// reduce: wirft Exception bei leerer Liste
// emptyList<Int>().reduce { acc, x -> acc + x } // UnsupportedOperationException!
// Lösung: reduceOrNull
val safest = emptyList<Int>().reduceOrNull { acc, x -> acc + x }
println(safest) // null