Manipulasi Map Lanjutan
putIfAbsent, update, map transformasi, dan menggabung map.
Lebih dari sekadar simpan-ambil
Modul map dasar mengajarkan akses dan assignment. Tapi Map punya method canggih untuk pola umum: "isi jika belum ada", "update berdasarkan nilai lama", "ubah semua nilai", "gabung dua map".
Analogi: map dasar itu seperti loker (simpan/ambil). Method lanjutan itu seperti resepsionis pintar: "kalau loker kosong, isi dengan ini", "kalau sudah ada, gabungkan".
Kenapa method ini ada?
Karena pola "cek lalu isi" ditulis manual rawan race logika dan bertele-tele. Method bawaan melakukannya atomik dalam satu panggilan yang jelas.
putIfAbsent dan update
void main() {
var stok = {'apel': 10, 'mangga': 5};
// putIfAbsent: isi HANYA jika kunci belum ada
stok.putIfAbsent('apel', () => 99); // diabaikan, sudah ada
stok.putIfAbsent('pisang', () => 7); // ditambahkan
print(stok); // {apel: 10, mangga: 5, pisang: 7}
// update: ubah berdasarkan nilai LAMA
stok.update('apel', (lama) => lama + 5); // 10 -> 15
print(stok['apel']); // 15
// update dengan ifAbsent: aman untuk kunci baru
stok.update('durian', (lama) => lama + 1, ifAbsent: () => 1);
print(stok['durian']); // 1
}Perhatikan parameternya berupa fungsi: () => 7 dan (lama) => lama + 5. Fungsi hanya dipanggil jika dibutuhkan (lazy), sehingga komputasi mahal tidak sia-sia.
Contoh kedua, menghitung frekuensi kata (kasus klasik):
void main() {
var kata = ['apel', 'mangga', 'apel', 'pisang', 'apel', 'mangga'];
var frekuensi = <String, int>{};
for (var k in kata) {
frekuensi.update(k, (lama) => lama + 1, ifAbsent: () => 1);
}
print(frekuensi); // {apel: 3, mangga: 2, pisang: 1}
}Satu baris update menggantikan if-else manual 4 baris. Pola ini muncul di analitik, voting, dan agregasi.
map(): transformasi pasangan
void main() {
var harga = {'apel': 5000, 'mangga': 8000};
// Ubah semua nilai: tambah pajak 10%
var denganPajak = harga.map((k, v) => MapEntry(k, (v * 1.1).toInt()));
print(denganPajak); // {apel: 5500, mangga: 8800}
// Ubah kunci jadi kapital
var kapital = harga.map((k, v) => MapEntry(k.toUpperCase(), v));
print(kapital); // {APEL: 5000, MANGGA: 8000}
}map pada Map mengembalikan Map baru lewat MapEntry(kunciBaru, nilaiBaru). Mirip map pada List, tapi untuk pasangan.
Menggabung map: addAll dan spread
void main() {
var a = {'x': 1, 'y': 2};
var b = {'y': 99, 'z': 3};
// addAll: timpa ke map pertama (mutatif)
var gabung1 = {...a};
gabung1.addAll(b);
print(gabung1); // {x: 1, y: 99, z: 3}
// spread: buat map baru, yang terakhir menang
var gabung2 = {...a, ...b};
print(gabung2); // {x: 1, y: 99, z: 3}
}Kunci duplikat selalu dimenangkan yang terakhir. Ini cara idiomatis menggabung konfigurasi default dengan konfigurasi pengguna.
Kesalahan umum
1. update tanpa ifAbsent untuk kunci baru
void main() {
var m = <String, int>{};
// m.update('a', (v) => v + 1); // ERROR: kunci tidak ada!
m.update('a', (v) => v + 1, ifAbsent: () => 1); // BENAR
print(m); // {a: 1}
}2. putIfAbsent dengan nilai langsung
void main() {
var m = <String, int>{};
// m.putIfAbsent('a', 5); // ERROR: butuh fungsi!
m.putIfAbsent('a', () => 5); // BENAR
print(m);
}Parameternya fungsi agar komputasi malas: jika kunci sudah ada, fungsi tidak dipanggil.
3. Mengira map() mengubah asli
Seperti List, map pada Map mengembalikan Map BARU. Simpan hasilnya.
Kesimpulan
putIfAbsent (isi jika kosong), update (ubah berdasar nilai lama + ifAbsent), map dengan MapEntry (transformasi), spread/addAll (gabung). Empat perkakas ini menuntaskan 90% manipulasi map tingkat lanjut.
Catatan teknis:
updatebersifat atomik untuk satu pemanggilan, aman dari kondisi balap dalam kode sinkron. Untuk kode async konkuren, pertimbangkan sinkronisasi tambahan.
Tantangan
Penghitung suara
Dari list suara = ['A','B','A','C','B','A'], hitung perolehan tiap kandidat dengan update + ifAbsent. Cetak map hasilnya dan pemenangnya (pakai reduce pada entries).
void main() {
var suara = ['A', 'B', 'A', 'C', 'B', 'A'];
var hasil = <String, int>{};
// TODO: update + ifAbsent
// TODO: pemenang = entries.reduce(...)
}Kuis Bab
Uji pemahamanmu: Bab 3: Fungsi Lanjut & Collection
Jawab 5 soal berikut, lalu tekan "Periksa Jawaban".
1.Diberikan void sapa({String nama = 'Tamu'}), bagaimana cara memanggilnya dengan nama 'Budi'?
2.Manakah penulisan arrow function yang valid di Dart?
3.Apa output dari: var l = [1, 2, 3]; l.add(4); print(l.length);
4.Apa isi list b setelah: var a = [1, 2]; var b = [...a, 3];
5.Struktur data mana yang menyimpan pasangan kunci-nilai (key-value)?