filter adalah operasi paling sering dipakai untuk memilih elemen. Namun banyak skenario memerlukan varian yang lebih spesifik: membuang null, memilih berdasarkan indeks, atau memecah list menjadi dua berdasarkan predikat. Pada chapter ini kita akan mempelajari varian filter yang tersedia di pustaka standar Kotlin dan kapan masing-masing lebih tepat dipakai.
Varian filter #
Tabel berikut merangkum varian yang paling sering dipakai:
| Fungsi | Mengembalikan | Kegunaan |
|---|---|---|
filter { pred } | Elemen yang memenuhi predikat | Seleksi umum |
filterNot { pred } | Elemen yang TIDAK memenuhi predikat | Kebalikan filter |
filterNotNull() | Elemen non-null | Buang null dari List<T?> |
filterIndexed { i, x -> ... } | Elemen yang memenuhi predikat berbasis indeks | Perlu tahu posisi |
filterIsInstance<T>() | Elemen bertipe T | Seleksi tipe dari List<Any> |
partition { pred } | Pair<List, List> (cocok, tidak) | Pecah jadi dua |
takeWhile { pred } | Awal list selama predikat true | Ambil prefix |
dropWhile { pred } | Sisa setelah prefix takeWhile | Lewati prefix |
takeLastWhile / dropLastWhile | Sama, dari akhir | Sufiks |
filterNotNull dan filterIsInstance #
filterNotNull() berguna pada List<T?> ketika kita ingin membuang elemen null dan mendapatkan List<T> non-nullable. filterIsInstance<T>() melakukan dua hal sekaligus: memfilter tipe dan menghasilkan tipe yang lebih spesifik.
val mungkin: List<Int?> = listOf(1, null, 2, null, 3)
val pasti: List<Int> = mungkin.filterNotNull()
val campur: List<Any> = listOf("a", 1, "b", 2.5, "c")
val stringSaja: List<String> = campur.filterIsInstance<String>()partition: Pecah Jadi Dua #
partition mengembalikan Pair<List<T>, List<T>> — elemen yang memenuhi predikat dan yang tidak. Dengan destructuring, kita bisa mendapatkan dua list sekaligus tanpa dua kali iterasi:
val (genap, ganjil) = (1..10).toList().partition { it % 2 == 0 }
// genap = [2, 4, 6, 8, 10], ganjil = [1, 3, 5, 7, 9]takeWhile dan dropWhile #
takeWhile mengambil elemen dari awal selama predikat true, lalu berhenti begitu predikat false — bahkan jika ada elemen setelahnya yang juga memenuhi predikat. dropWhile adalah kebalikannya: melewatkan prefix yang memenuhi predikat lalu mengembalikan sisanya.
val data = listOf(1, 2, 3, 0, 4, 5)
data.takeWhile { it > 0 } // [1, 2, 3] — berhenti di 0
data.dropWhile { it > 0 } // [0, 4, 5] — lewati 1,2,3Contoh Lengkap #
fun main() {
val angka = listOf(1, 2, 0, 3, null, 4, -5, 6)
println("Awal: $angka")
// filter: pertahankan elemen yang memenuhi predikat
val positif = angka.filter { it != null && it > 0 }
println("Positif: $positif")
// filterNotNull: buang elemen null
val tanpaNull: List<Int> = angka.filterNotNull()
println("Tanpa null: $tanpaNull")
// filterNot: kebalikan dari filter
val bukanPositif = tanpaNull.filterNot { it > 0 }
println("Bukan positif: $bukanPositif")
// filterIndexed: predikat menerima indeks juga
val ambilGenapIndeks = tanpaNull.filterIndexed { i, _ -> i % 2 == 0 }
println("Indeks genap: $ambilGenapIndeks")
// partition: pecah jadi dua list (cocok, tidak cocok)
val (genap, ganjil) = tanpaNull.partition { it % 2 == 0 }
println("Genap: $genap")
println("Ganjil: $ganjil")
// takeWhile: ambil dari awal selama predikat true
val data = listOf(1, 2, 3, 0, 4, 5)
val ambilMaju = data.takeWhile { it > 0 }
println("takeWhile >0: $ambilMaju")
// dropWhile: lewati elemen dari awal selama predikat true
val lewati = data.dropWhile { it > 0 }
println("dropWhile >0: $lewati")
// takeLastWhile dan dropLastWhile
val ambilBelakang = listOf(1, 2, 0, 3, 4, 5).takeLastWhile { it > 0 }
println("takeLastWhile >0: $ambilBelakang")
// filterIsInstance: pilih berdasarkan tipe
val campuran: List<Any> = listOf("a", 1, "b", 2.5, "c", 3L)
val stringSaja = campuran.filterIsInstance<String>()
println("Hanya String: $stringSaja")
val intSaja = campuran.filterIsInstance<Int>()
println("Hanya Int: $intSaja")
// filter bersarang: kombinasi
val nilai = listOf(80, 50, 90, 70, 40, 85, 60)
val lulusBaik = nilai.filter { it >= 70 }.filter { it < 90 }
println("Lulus 70..89: $lulusBaik")
}Output yang Dihasilkan:
Awal: [1, 2, 0, 3, null, 4, -5, 6]
Positif: [1, 2, 3, 4, 6]
Tanpa null: [1, 2, 0, 3, 4, -5, 6]
Bukan positif: [0, -5]
Indeks genap: [1, 0, 4, 6]
Genap: [2, 0, 4, 6]
Ganjil: [1, 3, -5]
takeWhile >0: [1, 2, 3]
dropWhile >0: [0, 4, 5]
takeLastWhile >0: [3, 4, 5]
Hanya String: [a, b, c]
Hanya Int: [1]
Lulus 70..89: [80, 70, 85]Mari kita bedah baris-baris penting di atas:
angka.filter { it != null && it > 0 }: karena elemenInt?, kita perlu memeriksa non-null sebelum membandingkan. Hasil tetapList<Int?>— tidak ada penyempitan tipe. Untuk mendapatList<Int>, gunakanfilterNotNull().angka.filterNotNull(): membuang elemennullsekaligus menyempitkan tipe dariList<Int?>menjadiList<Int>. Operasi ini lebih ringkas dan aman secara tipe daripadafilter { it != null }.tanpaNull.filterNot { it > 0 }:filterNotadalah kebalikanfilter. Ekuivalen denganfilter { !(it > 0) }tetapi lebih mudah dibaca untuk predikat kompleks.tanpaNull.filterIndexed { i, _ -> i % 2 == 0 }: lambda menerima(indeks, elemen). Karena kita hanya peduli indeks, parameter elemen dinamai_(placeholder).tanpaNull.partition { it % 2 == 0 }: mengembalikanPair<List<Int>, List<Int>>. Destructuringval (genap, ganjil) = ...mengekstrak kedua list secara ringkas.data.takeWhile { it > 0 }: mengambil[1, 2, 3]— begitu menemui0, iterasi berhenti. Elemen4, 5setelah0tidak diambil meskipun> 0.campuran.filterIsInstance<String>(): hanya elemen bertipeStringyang masuk hasil. Tipe hasil disempitkan menjadiList<String>— bukanList<Any>.nilai.filter { it >= 70 }.filter { it < 90 }: duafilterberturut-turut menghasilkan irisan predikat. Bisa juga digabung menjadi satufilter { it in 70..89 }untuk efisiensi.
Referensi Lanjutan #
- Collection filtering
— panduan resmi
filter,filterNot,filterNotNull,partition - Collection parts
— operasi
slice,take,drop,chunked
Setelah memahami seleksi elemen, kita akan beralih ke transformasi struktural: map, flatMap, zip, associate, dan kawan-kawannya akan dibahas pada chapter transforming collections.