Setelah memahami seleksi elemen, kita sering perlu mengubah struktur collection: memetakan nilai, meratakan list bersarang, memasangkan dua list, atau membentuk map dari list. Pada chapter ini kita akan mempelajari transformasi struktural paling penting: map, flatMap, zip, unzip, associate, groupBy, chunked, dan windowed. Setiap fungsi memiliki pola penggunaan khas yang akan kita lihat dalam contoh konkret.
map dan Variannya #
map adalah transformasi satu-ke-satu: tiap elemen input menjadi satu elemen output. Variannya menangani kasus khusus:
| Fungsi | Mengembalikan | Catatan |
|---|---|---|
map { it -> ... } | List<R> | Transformasi satu-ke-satu |
mapIndexed { i, x -> ... } | List<R> | Lambda menerima indeks |
mapNotNull { ... } | List<R> | Membuang hasil null |
mapIndexedNotNull { i, x -> ... } | List<R> | Gabungan dua di atas |
val kuadrat = listOf(1, 2, 3).map { it * it } // [1, 4, 9]
val parsed = listOf("1", "abc", "3").mapNotNull { it.toIntOrNull() } // [1, 3]flatMap dan flatten #
flatMap melakukan dua langkah: pertama, lambda mengubah tiap elemen menjadi collection; kedua, semua collection tersebut diratakan menjadi satu list. flatten adalah versi sederhana yang hanya meratakan list bersarang tanpa transformasi.
val kalimat = listOf("Halo Dunia", "Belajar Kotlin")
val kata = kalimat.flatMap { it.split(" ") }
// [Halo, Dunia, Belajar, Kotlin]
val grup = listOf(listOf(1, 2), listOf(3, 4))
val rata = grup.flatten() // [1, 2, 3, 4]zip dan unzip #
zip memasangkan dua collection berdasarkan indeks, menghasilkan List<Pair<A, B>>. Jika panjang berbeda, hasil mengikuti yang lebih pendek. unzip adalah kebalikannya: List<Pair<K, V>> menjadi Pair<List<K>, List<V>>.
val nama = listOf("Budi", "Siti", "Dewi")
val umur = listOf(25, 30, 22)
val pasangan = nama.zip(umur) // [(Budi,25), (Siti,30), (Dewi,22)]
val gabungan = nama.zip(umur) { n, u -> "$n=$u" } // custom transformassociate, associateWith, associateBy #
Tiga fungsi ini mengubah list menjadi Map:
| Fungsi | Mengembalikan | Logika |
|---|---|---|
associate { x -> Pair(k, v) } | Map<K, V> | Lambda mengembalikan pair |
associateWith { x -> v } | Map<T, V> | Key = elemen, value = lambda |
associateBy { x -> k } | Map<K, T> | Key = lambda, value = elemen |
val nama = listOf("Budi", "Siti", "Dewi")
val petaPanjang = nama.associateWith { it.length } // {Budi=4, Siti=4, Dewi=4}
val petaByKey = nama.associateBy { it.first() } // {B=Budi, S=Siti, D=Dewi}groupBy #
groupBy { key } mengelompokkan elemen berdasarkan kunci yang dihasilkan lambda, menghasilkan Map<K, List<T>>. Operasi ini sangat berguna untuk kategorisasi.
val buah = listOf("Apel", "Anggur", "Mangga", "Alpukat")
val perHuruf = buah.groupBy { it.first() }
// {A=[Apel, Anggur, Alpukat], M=[Mangga]}chunked dan windowed #
Kedua fungsi ini memecah list menjadi potongan-potongan:
| Fungsi | Perilaku | Hasil dari (1..5).chunked(2) |
|---|---|---|
chunked(n) | Potongan non-overlap ukuran n | [[1,2], [3,4], [5]] |
windowed(n) | Jendela geser ukuran n, overlap 1 | [[1,2,3], [2,3,4], [3,4,5]] |
windowed(n, step=m) | Langkah m | Bebas |
Contoh Lengkap #
fun main() {
val angka = listOf(1, 2, 3, 4, 5)
// map: transformasi satu-ke-satu
val kuadrat = angka.map { it * it }
println("map kuadrat: $kuadrat")
// mapIndexed: akses indeks + nilai
val denganPosisi = angka.mapIndexed { i, v -> "[$i]=$v" }
println("mapIndexed: $denganPosisi")
// mapNotNull: buang hasil null
val aman = listOf("1", "2", "abc", "4")
val parsed = aman.mapNotNull { it.toIntOrNull() }
println("mapNotNull: $parsed")
// flatMap: tiap elemen jadi collection, lalu diratakan
val kalimat = listOf("Halo Dunia", "Belajar Kotlin")
val kata = kalimat.flatMap { it.split(" ") }
println("flatMap kata: $kata")
// Contoh lain: deret ke sub-list
val grup = listOf(listOf(1, 2), listOf(3), listOf(4, 5, 6))
val rata = grup.flatten()
println("flatten: $rata")
// zip: pasangkan dua list berdasarkan indeks
val nama = listOf("Budi", "Siti", "Dewi")
val umur = listOf(25, 30, 22)
val pasangan = nama.zip(umur)
println("zip: $pasangan")
pasangan.forEach { (n, u) -> println(" $n umur $u") }
// zip dengan transformasi
val gabungan = nama.zip(umur) { n, u -> "$n=$u" }
println("zip transform: $gabungan")
// unzip: pecah list pair jadi dua list
val (nama2, umur2) = pasangan.unzip()
println("unzip nama: $nama2")
println("unzip umur: $umur2")
// associate: ubah ke map
val petaUmur = nama.zip(umur).toMap()
println("toMap: $petaUmur")
val petaPanjang = nama.associateWith { it.length }
println("associateWith: $petaPanjang")
val petaByKey = nama.associateBy { it.take(1) }
println("associateBy: $petaByKey")
// groupBy: kumpulkan per kunci
val mahasiswa = listOf("Budi", "Siti", "Bunga", "Budi", "Andi")
val perHuruf = mahasiswa.groupBy { it.first() }
println("groupBy huruf awal: $perHuruf")
// chunked: potong ukuran tetap
val angkaPanjang = (1..10).toList()
val potongan = angkaPanjang.chunked(3)
println("chunked(3): $potongan")
// windowed: jendela geser
val jendela = angkaPanjang.windowed(3)
println("windowed(3): $jendela")
val jendelaLangkah = angkaPanjang.windowed(3, step = 2)
println("windowed(3, step=2): $jendelaLangkah")
}Output yang Dihasilkan:
map kuadrat: [1, 4, 9, 16, 25]
mapIndexed: [[0]=1, [1]=2, [2]=3, [3]=4, [4]=5]
mapNotNull: [1, 2, 4]
flatMap kata: [Halo, Dunia, Belajar, Kotlin]
flatten: [1, 2, 3, 4, 5, 6]
zip: [(Budi, 25), (Siti, 30), (Dewi, 22)]
Budi umur 25
Siti umur 30
Dewi umur 22
zip transform: [Budi=25, Siti=30, Dewi=22]
unzip nama: [Budi, Siti, Dewi]
unzip umur: [25, 30, 22]
toMap: {Budi=25, Siti=30, Dewi=22}
associateWith: {Budi=4, Siti=4, Dewi=4}
associateBy: {B=Budi, S=Siti, D=Dewi}
groupBy huruf awal: {B=[Budi, Bunga, Budi], S=[Siti], A=[Andi]}
chunked(3): [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
windowed(3): [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]]
windowed(3, step=2): [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]]Mari kita bedah baris-baris penting di atas:
angka.map { it * it }: tiap elemenIntdiubah menjadiIntlain (kuadratnya). Lambda mengembalikan nilai baru; list asli tidak berubah.aman.mapNotNull { it.toIntOrNull() }: lambda mengembalikanInt?—nulluntuk string non-numerik.mapNotNullmembuangnulldan menyempitkan tipe menjadiList<Int>.kalimat.flatMap { it.split(" ") }:split(" ")menghasilkanList<String>per kalimat.flatMapmeratakan kedua list menjadi satu[Halo, Dunia, Belajar, Kotlin].nama.zip(umur): pasangan(Budi, 25),(Siti, 30),(Dewi, 22). Bilaumurlebih pendek, elemennamayang berlebih akan diabaikan.pasangan.unzip(): pecahList<Pair<String, Int>>menjadiPair<List<String>, List<Int>>. Destructuringval (nama2, umur2) = ...mengambil kedua list.nama.associateWith { it.length }: elemen list menjadi key, panjangnya menjadi value. Hasil{Budi=4, Siti=4, Dewi=4}.nama.associateBy { it.first() }: karakter pertama menjadi key. Bila ada duplikat (misalBudidanBunga), nilai terakhir menimpa yang sebelumnya.mahasiswa.groupBy { it.first() }: menghasilkanMap<Char, List<String>>. Semua nama berawalanBterkumpul dalam satu list value.angkaPanjang.chunked(3): potongan non-overlap. Potongan terakhir[10]hanya berisi satu elemen karena tidak cukup.angkaPanjang.windowed(3): jendela geser dengan overlap. Setiap jendela berisi 3 elemen berturut-turut, total 8 jendela dari list 10 elemen.
Referensi Lanjutan #
- Collection transformations
— panduan resmi
map,flatMap,zip,associate - Collection operations — tinjauan umum operasi transformasi
Setelah transformasi satu collection, kita akan melihat agregasi berbasis kunci yang lebih kaya: groupBy, groupingBy, fold, dan reduce akan dibahas pada chapter grouping.