Agregasi adalah proses meringkas collection menjadi satu nilai atau objek kecil. Dari daftar transaksi, kita mungkin ingin tahu total pendapatan, jumlah produk, harga tertinggi, atau rata-rata qty per produk. Pada chapter ini kita akan mempelajari operasi agregasi utama: sum, sumOf, average, maxOrNull, minOrNull, maxByOrNull, minByOrNull, reduce, dan fold. Kita juga akan melihat pola agregasi multi-nilai yang menggunakan satu fold untuk menghitung beberapa statistik sekaligus.
Operasi Numerik Dasar #
Untuk collection tipe Number, tersedia operasi numerik langsung:
| Operasi | Mengembalikan | Catatan |
|---|---|---|
sum() | Int, Long, Double | Total elemen |
average() | Double | Rata-rata; NaN untuk empty |
maxOrNull() | T? | Elemen terbesar atau null |
minOrNull() | T? | Elemen terkecil atau null |
maxByOrNull { sel } | T? | Elemen dengan selector terbesar |
minByOrNull { sel } | T? | Elemen dengan selector terkecil |
maxOfOrNull { sel } | R? | Nilai selector terbesar |
minOfOrNull { sel } | R? | Nilai selector terkecil |
maxOf dan minOf tanpa OrNull melempar NoSuchElementException pada collection kosong.
sumOf: Selector Numerik Ringkas #
sumOf { it.qty } menggantikan pola map { it.qty }.sum() dengan satu iterasi. Tersedia overload untuk Int, Long, Double, BigInteger, dan tipe numerik lain.
data class Penjualan(val produk: String, val qty: Int, val harga: Int)
val totalQty = data.sumOf { it.qty }
val totalPendapatan = data.sumOf { it.qty * it.harga }sumOf lebih efisien daripada map.sum() karena tidak mengalokasikan list sementara.
reduce vs fold #
reduce dan fold adalah dua operasi paling fleksibel. Perbedaan utama:
| Aspek | reduce | fold |
|---|---|---|
| Nilai awal | Elemen pertama | Eksplisit (bisa berbeda tipe) |
| Tipe hasil | Sama dengan elemen | Bebas, mengikuti nilai awal |
| Pada collection kosong | Melempar eksepsi | Mengembalikan nilai awal |
val totalReduce = listOf(1, 2, 3, 4).reduce { acc, x -> acc + x } // 10
val totalFold = listOf(1, 2, 3, 4).fold(0) { acc, x -> acc + x } // 10
val ringkasan = listOf(1, 2, 3).fold("Deret:") { acc, x -> "$acc $x" } // "Deret: 1 2 3"Contoh Lengkap #
data class Penjualan(val produk: String, val qty: Int, val harga: Int) {
val subtotal get() = qty * harga
}
fun main() {
val data = listOf(
Penjualan("Apel", 3, 5000),
Penjualan("Jeruk", 5, 4000),
Penjualan("Pisang", 10, 3000),
Penjualan("Mangga", 2, 8000)
)
// sum: hanya untuk collection of numbers
val qtyList = data.map { it.qty }
println("Sum qty: ${qtyList.sum()}")
// sumOf: ringkas untuk selector numerik
val totalQty = data.sumOf { it.qty }
val totalSubtotal = data.sumOf { it.subtotal }
println("Total qty: $totalQty")
println("Total subtotal: $totalSubtotal")
// average
val avgQty = qtyList.average()
println("Rata-rata qty: $avgQty")
val avgSubtotal = data.map { it.subtotal }.average()
println("Rata-rata subtotal: $avgSubtotal")
// maxOrNull / minOrNull
val maxQty = data.maxOfOrNull { it.qty }
val minQty = data.minOfOrNull { it.qty }
println("Max qty: $maxQty, Min qty: $minQty")
val maxProdukByQty = data.maxByOrNull { it.qty }
println("Produk qty tertinggi: $maxProdukByQty")
val minProdukBySubtotal = data.minByOrNull { it.subtotal }
println("Produk subtotal terendah: $minProdukBySubtotal")
// maxOf / minOf (melempar pada koleksi kosong)
val dataIsi = listOf(5, 2, 8, 1, 9)
println("maxOf: ${dataIsi.maxOf { it }}")
println("minOf: ${dataIsi.minOf { it }}")
// reduce: akumulasi tanpa nilai awal
val totalReduce = data.map { it.subtotal }.reduce { acc, x -> acc + x }
println("Total via reduce: $totalReduce")
// fold: akumulasi dengan nilai awal dan tipe bebas
val ringkasan = data.fold("Ringkasan: ") { acc, p -> "$acc${p.produk}=${p.subtotal}; " }
println(ringkasan)
// count
println("Jumlah produk: ${data.count()}")
println("Jumlah produk qty >=5: ${data.count { it.qty >= 5 }}")
// Contoh agregasi multi-nilai dengan fold
data class Stat(val total: Int, val max: Int, val min: Int)
val stat = data.fold(Stat(0, Int.MIN_VALUE, Int.MAX_VALUE)) { acc, p ->
Stat(
total = acc.total + p.subtotal,
max = maxOf(acc.max, p.subtotal),
min = minOf(acc.min, p.subtotal)
)
}
println("Statistik: $stat")
// Empty collection
val kosong = emptyList<Int>()
println("Empty sum: ${kosong.sum()}")
println("Empty maxOrNull: ${kosong.maxOrNull()}")
println("Empty average: ${kosong.average()}")
// println("Empty maxOf: ${kosong.maxOf { it }}") // melempar NoSuchElementException
}Output yang Dihasilkan:
Sum qty: 20
Total qty: 20
Total subtotal: 81000
Rata-rata qty: 5.0
Rata-rata subtotal: 20250.0
Max qty: 10, Min qty: 2
Produk qty tertinggi: Penjualan(produk=Pisang, qty=10, harga=3000)
Produk subtotal terendah: Penjualan(produk=Apel, qty=3, harga=5000)
maxOf: 9
minOf: 1
Total via reduce: 81000
Ringkasan: Apel=15000; Jeruk=20000; Pisang=30000; Mangga=16000;
Jumlah produk: 4
Jumlah produk qty >=5: 2
Statistik: Stat(total=81000, max=30000, min=15000)
Empty sum: 0
Empty maxOrNull: null
Empty average: NaNMari kita bedah baris-baris penting di atas:
data.sumOf { it.qty }: menjumlahkanqtytanpa membuat list antara. Bila kita menulisdata.map { it.qty }.sum(), ada alokasi list tambahan;sumOfmenghindari itu.qtyList.average(): mengembalikanDouble— selaluDouble, bahkan bila elemenInt. Pada list kosong, hasilnyaNaN(bukan eksepsi).data.maxOfOrNull { it.qty }: versi aman yang mengembalikanInt?—nullbila list kosong.maxOftanpaOrNullakan melemparNoSuchElementException.data.maxByOrNull { it.qty }: mengembalikan elemen (objekPenjualan) yang memilikiqtytertinggi, bukan nilaiqtyitu sendiri.data.fold("Ringkasan: ") { acc, p -> ... }: tipe akumulatorString, berbeda dari tipe elemenPenjualan. Inilah keunggulanfolddibandingreduce.data.fold(Stat(0, ...)) { acc, p -> Stat(...) }: pola agregasi multi-nilai. Satu iterasi menghasilkan tiga statistik sekaligus: total, max, dan min. Pola ini lebih efisien daripada tigafoldterpisah yang masing-masing mengiterasi list.kosong.average()mengembalikanNaN: pilihan desain pustaka standar untuk menghindari eksepsi pada collection kosong. Selalu periksaisEmpty()sebelum memproses rata-rata bila ingin menangani kasus ini secara khusus.
Referensi Lanjutan #
- Collection aggregate operations
— panduan resmi
sum,count,average,min,max,reduce,fold - Collection operations — tinjauan umum operasi agregasi
Setelah memahami agregasi, kita akan melihat lebih dekat cara iterasi collection — dari for tradisional hingga iterator protocol — pada chapter berikutnya.