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:

FungsiMengembalikanKegunaan
filter { pred }Elemen yang memenuhi predikatSeleksi umum
filterNot { pred }Elemen yang TIDAK memenuhi predikatKebalikan filter
filterNotNull()Elemen non-nullBuang null dari List<T?>
filterIndexed { i, x -> ... }Elemen yang memenuhi predikat berbasis indeksPerlu tahu posisi
filterIsInstance<T>()Elemen bertipe TSeleksi tipe dari List<Any>
partition { pred }Pair<List, List> (cocok, tidak)Pecah jadi dua
takeWhile { pred }Awal list selama predikat trueAmbil prefix
dropWhile { pred }Sisa setelah prefix takeWhileLewati prefix
takeLastWhile / dropLastWhileSama, dari akhirSufiks

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.

kotlin Contoh: filterNotNull dan filterIsInstance
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:

kotlin Contoh: partition
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.

kotlin Contoh: takeWhile dan dropWhile
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,3

Contoh Lengkap #

kotlin
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:

text
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:

  1. angka.filter { it != null && it > 0 }: karena elemen Int?, kita perlu memeriksa non-null sebelum membandingkan. Hasil tetap List<Int?> — tidak ada penyempitan tipe. Untuk mendapat List<Int>, gunakan filterNotNull().
  2. angka.filterNotNull(): membuang elemen null sekaligus menyempitkan tipe dari List<Int?> menjadi List<Int>. Operasi ini lebih ringkas dan aman secara tipe daripada filter { it != null }.
  3. tanpaNull.filterNot { it > 0 }: filterNot adalah kebalikan filter. Ekuivalen dengan filter { !(it > 0) } tetapi lebih mudah dibaca untuk predikat kompleks.
  4. tanpaNull.filterIndexed { i, _ -> i % 2 == 0 }: lambda menerima (indeks, elemen). Karena kita hanya peduli indeks, parameter elemen dinamai _ (placeholder).
  5. tanpaNull.partition { it % 2 == 0 }: mengembalikan Pair<List<Int>, List<Int>>. Destructuring val (genap, ganjil) = ... mengekstrak kedua list secara ringkas.
  6. data.takeWhile { it > 0 }: mengambil [1, 2, 3] — begitu menemui 0, iterasi berhenti. Elemen 4, 5 setelah 0 tidak diambil meskipun > 0.
  7. campuran.filterIsInstance<String>(): hanya elemen bertipe String yang masuk hasil. Tipe hasil disempitkan menjadi List<String> — bukan List<Any>.
  8. nilai.filter { it >= 70 }.filter { it < 90 }: dua filter berturut-turut menghasilkan irisan predikat. Bisa juga digabung menjadi satu filter { it in 70..89 } untuk efisiensi.

Referensi Lanjutan #

Setelah memahami seleksi elemen, kita akan beralih ke transformasi struktural: map, flatMap, zip, associate, dan kawan-kawannya akan dibahas pada chapter transforming collections.