BAB 14: Map — Pencarian Data Berdasarkan Nama
Pelajari map di Go: struktur data key-value yang memungkinkan pencarian data berdasarkan nama. Pahami cara deklarasi, akses, pengecekan keberadaan key, modifikasi, dan kapan memilih map vs slice.
Slice di bab sebelumnya memungkinkan kamu menyimpan banyak data, tapi cara aksesnya selalu lewat nomor posisi — buah[0], buah[1], dan seterusnya. Itu tidak masalah ketika urutannya penting. Tapi bagaimana kalau kamu ingin mengambil nilai ujian siswa bernama “Budi” tanpa harus tahu dia ada di indeks berapa? Di sinilah map masuk.
Map adalah struktur data yang menyimpan pasangan key dan value. Kamu memberi nama (key) untuk setiap data, dan mengambilnya kembali dengan nama yang sama. Analoginya persis seperti kamus: kamu cari kata (key), dapat artinya (value) — tanpa perlu membuka dari halaman pertama.
Deklarasi dan Inisialisasi
Ada dua cara utama membuat map di Go.
// main.go
package main
import "fmt"
func main() {
// cara 1: literal — langsung dengan nilai awal
nilaiSiswa := map[string]int{
"Budi": 85,
"Sari": 92,
"Andi": 78,
}
fmt.Println("nilai siswa:", nilaiSiswa)
// cara 2: make — buat map kosong siap pakai
inventaris := make(map[string]int)
inventaris["pensil"] = 50
inventaris["buku"] = 30
fmt.Println("inventaris:", inventaris)
}
nilai siswa: map[Andi:78 Budi:85 Sari:92]
inventaris: map[buku:30 pensil:50]
Jangan deklarasikan map dengan var m map[string]int tanpa make lalu langsung assign nilai. Map yang belum diinisialisasi nilainya nil dan akan menyebabkan panic saat kamu mencoba menulis ke dalamnya. Selalu gunakan literal {} atau make() sebelum mulai mengisi data.
Membaca dan Menulis Data
Membaca dan menulis map menggunakan sintaks yang sama dengan slice, tapi menggunakan key sebagai pengganti indeks numerik.
// main.go
package main
import "fmt"
func main() {
nilai := map[string]int{"Budi": 85, "Sari": 92}
// membaca
fmt.Println("Nilai Budi:", nilai["Budi"])
// menambah key baru
nilai["Rina"] = 88
fmt.Println("setelah tambah Rina:", nilai)
// memperbarui nilai yang sudah ada
nilai["Budi"] = 90
fmt.Println("setelah update Budi:", nilai)
}
Nilai Budi: 85
setelah tambah Rina: map[Budi:85 Rina:88 Sari:92]
setelah update Budi: map[Budi:90 Rina:88 Sari:92]
Go tidak membedakan antara “tambah key baru” dan “perbarui key yang sudah ada” — sintaksnya identik. Jika key sudah ada, nilainya ditimpa. Jika belum ada, key baru dibuat.
Mengecek Keberadaan Key
Membaca key yang tidak ada di map tidak menyebabkan error. Go mengembalikan zero value dari tipe value-nya — 0 untuk int, "" untuk string. Ini bisa menjebak: bagaimana kamu tahu apakah nilainya memang 0, atau keynya memang tidak ada?
Go menyelesaikan ini dengan two-value assignment:
// main.go
package main
import "fmt"
func main() {
nilai := map[string]int{"Budi": 85, "Sari": 92}
// two-value assignment: value dan boolean "ada atau tidak"
nilaiAndi, ada := nilai["Andi"]
if ada {
fmt.Println("Nilai Andi:", nilaiAndi)
} else {
fmt.Println("Andi tidak terdaftar")
}
// cara ringkas: langsung cek tanpa menyimpan value-nya
if _, ada := nilai["Sari"]; ada {
fmt.Println("Sari terdaftar")
}
}
Andi tidak terdaftar
Sari terdaftar
Variabel ada bertipe bool — true jika key ditemukan, false jika tidak. Selalu gunakan pola ini ketika kamu perlu membedakan antara “tidak ada” dan “ada tapi nilainya zero value”.
Menghapus Data
Fungsi bawaan delete menghapus key dari map. Aman dipanggil meski keynya tidak ada.
// main.go
package main
import "fmt"
func main() {
nilai := map[string]int{"Budi": 85, "Sari": 92, "Rina": 88}
fmt.Println("sebelum:", nilai)
delete(nilai, "Budi")
fmt.Println("sesudah hapus Budi:", nilai)
// hapus key yang tidak ada — tidak error
delete(nilai, "Andi")
fmt.Println("hapus 'Andi' (tidak ada):", nilai)
}
sebelum: map[Budi:85 Rina:88 Sari:92]
sesudah hapus Budi: map[Rina:88 Sari:92]
hapus 'Andi' (tidak ada): map[Rina:88 Sari:92]
Iterasi Map dengan for range
Iterasi map mirip dengan slice, tapi variabel pertama adalah key, bukan indeks numerik.
// main.go
package main
import "fmt"
func main() {
nilai := map[string]int{"Budi": 85, "Sari": 92, "Rina": 88, "Andi": 78}
total := 0
for nama, skor := range nilai {
fmt.Printf(" %-6s: %d\n", nama, skor)
total += skor
}
rataRata := float64(total) / float64(len(nilai))
fmt.Printf("Rata-rata kelas: %.1f\n", rataRata)
}
Budi : 85
Sari : 92
Rina : 88
Andi : 78
Rata-rata kelas: 85.8
Urutan iterasi map di Go tidak dijamin. Setiap kali program dijalankan, urutan key yang muncul bisa berbeda. Jika kamu butuh iterasi yang terurut, kumpulkan key-nya ke dalam slice terlebih dahulu, sort, lalu iterasi slice-nya.
Map Bersarang
Map bisa menjadi value dari map lain. Ini berguna untuk merepresentasikan data hierarkis — misalnya nilai per mata pelajaran untuk setiap siswa.
// main.go
package main
import "fmt"
func main() {
rapor := map[string]map[string]int{
"Budi": {"Matematika": 85, "Fisika": 80, "Kimia": 78},
"Sari": {"Matematika": 92, "Fisika": 88, "Kimia": 91},
}
for siswa, mapel := range rapor {
fmt.Printf("%s:\n", siswa)
for pelajaran, nilai := range mapel {
fmt.Printf(" %-12s: %d\n", pelajaran, nilai)
}
}
}
Budi:
Matematika : 85
Fisika : 80
Kimia : 78
Sari:
Matematika : 92
Fisika : 88
Kimia : 91
Saat menggunakan map bersarang, pastikan inner map sudah diinisialisasi sebelum diisi. rapor["Budi"] di atas sudah diinisialisasi sebagai bagian dari literal — tapi jika kamu menambah siswa baru secara dinamis, kamu perlu make(map[string]int) secara eksplisit untuk inner map-nya.
Latihan
Latihan 1 — Frekuensi kata:
Dari slice []string{"apel", "jeruk", "apel", "mangga", "jeruk", "apel"}, hitung berapa kali setiap buah muncul menggunakan map. Tampilkan hasilnya beserta buah yang paling sering muncul.
Latihan 2 — Konversi slice ke map:
Dari dua slice paralel nama := []string{"Budi", "Sari", "Andi"} dan nilai := []int{85, 92, 78}, buat map map[string]int yang menghubungkan nama dengan nilainya. Kemudian tambahkan fungsi lookup: jika nama diinput, tampilkan nilainya atau pesan “tidak ditemukan”.
Latihan 3 — Hapus nilai di bawah rata-rata: Dari map nilai siswa yang kamu buat di Latihan 2, hitung rata-rata kelas, lalu hapus semua siswa yang nilainya di bawah rata-rata. Tampilkan map sebelum dan sesudah operasi ini.
Dengan map, kamu sekarang punya tiga cara menyimpan koleksi data: array untuk data berukuran tetap, slice untuk data yang bisa tumbuh, dan map untuk pencarian berdasarkan nama. Ketiganya sering dipakai bersamaan dalam program nyata. Tapi sejauh ini semua logika kita tulis di satu tempat — di dalam main. Program yang lebih besar butuh cara untuk memecah logika menjadi bagian-bagian yang bisa digunakan kembali. Itulah yang akan kita pelajari di bab berikutnya: function.