Belajar Golang dari Nol #5: Slice dan Map, Kumpulan Data di Go
Di Belajar Golang dari Nol #4 kita sudah belajar membungkus data ke dalam struct dan menempelkan method padanya. Satu struct mewakili satu barang, satu user, atau satu produk. Tapi program nyata jarang mengurus satu data saja. Toko punya banyak produk. Sekolah punya banyak siswa. Kita butuh wadah untuk menampung kumpulan data. Di Go, dua wadah yang paling sering dipakai adalah slice dan map. Keduanya jadi menu utama kita hari ini.
Sekilas tentang array
Sebelum masuk ke slice, kenalan dulu dengan fondasinya, yaitu array. Array adalah deretan data dengan ukuran tetap. Sekali dibuat berisi 3 slot, selamanya 3 slot.
var nilai [3]int
nilai[0] = 80
nilai[1] = 75
nilai[2] = 90
Bayangkan rak dengan jumlah laci yang sudah dipatok dari pabrik. Mau menambah laci keempat, tidak bisa. Karena kaku seperti itu, array jarang dipakai langsung dalam kode Go sehari-hari. Yang hampir selalu dipakai adalah slice, yang di belakang layar sebenarnya menumpang pada array. Cukup tahu itu dulu. Fokus kita sekarang ke slice.
Slice, daftar yang bisa memanjang
Slice mirip daftar belanja di kertas. Awalnya berisi tiga barang. Ingat sesuatu, tulis lagi di bawahnya. Daftarnya memanjang sendiri tanpa perlu ganti kertas.
Membuat slice hampir sama dengan array, hanya tanpa angka di dalam kurung siku.
package main
import "fmt"
func main() {
buah := []string{"apel", "mangga", "jeruk"}
fmt.Println(buah) // [apel mangga jeruk]
fmt.Println(len(buah)) // 3
fmt.Println(buah[0]) // apel
fmt.Println(buah[2]) // jeruk
}
Tiga hal yang perlu dicatat dari kode di atas.
[]string{...}disebut slice literal. Kita langsung mengisi datanya saat membuat.len(buah)menghitung jumlah isi slice.buah[0]mengakses isi berdasarkan posisi. Posisi dihitung dari 0, bukan dari 1.
Untuk menambah isi, pakai append.
buah = append(buah, "salak")
fmt.Println(buah) // [apel mangga jeruk salak]
fmt.Println(len(buah)) // 4
Perhatikan polanya. Hasil append harus disimpan kembali ke variabelnya. Menulis append(buah, "salak") saja tanpa menampung hasilnya tidak akan mengubah apa pun, dan Go bahkan menolaknya saat compile. Anggap append seperti fotokopi daftar lama plus satu baris baru. Daftar barunya harus kita pegang.
Slice juga bisa dipotong. Sintaksnya a[awal:akhir]. Posisi awal ikut diambil, posisi akhir tidak.
potongan := buah[1:3]
fmt.Println(potongan) // [mangga jeruk]
buah[1:3] artinya ambil mulai posisi 1 sampai sebelum posisi 3. Jadi posisi 1 dan 2 saja. Awalnya sering bikin bingung, tapi lama-lama terasa natural.
Slice berisi struct
Di bagian 4 kita sudah bisa membuat struct Produk. Sekarang gabungkan. Slice tidak hanya bisa menampung string atau angka. Ia bisa menampung struct. Inilah cara Go menyimpan daftar produk, daftar siswa, atau daftar transaksi.
package main
import "fmt"
type Produk struct {
Nama string
Harga int
}
func main() {
daftar := []Produk{
{Nama: "Kopi Bubuk", Harga: 25000},
{Nama: "Gula Pasir", Harga: 15000},
{Nama: "Teh Celup", Harga: 10000},
}
for _, p := range daftar {
fmt.Println(p.Nama, "harganya Rp", p.Harga)
}
}
Baris for _, p := range daftar artinya kunjungi setiap isi slice satu per satu, lalu simpan isinya ke variabel p. Tanda _ dipakai karena kita tidak butuh nomor urutnya. Detail lengkap soal for dan range kita bahas tuntas di bagian 6. Sekarang cukup pahami bahwa pola ini dipakai untuk menelusuri isi slice.
Map, mencari data lewat kunci
Slice mencari data lewat posisi. Map mencari data lewat kunci. Bayangkan loker di kolam renang. Anda tidak menghafal loker keberapa dari kiri. Anda cukup pegang kuncinya, lalu langsung buka loker yang cocok. Kunci pada map bisa berupa string, misalnya nama barang, dan nilainya bisa apa saja.
Bentuk penulisannya map[tipeKunci]tipeNilai. Contoh map untuk mencatat stok barang.
package main
import "fmt"
func main() {
stok := map[string]int{
"kopi": 10,
"gula": 5,
}
// menambah data baru
stok["teh"] = 8
// mengubah data yang sudah ada
stok["kopi"] = 12
// menghapus data
delete(stok, "gula")
fmt.Println(stok) // map[kopi:12 teh:8]
fmt.Println(stok["teh"]) // 8
}
Menambah dan mengubah pakai sintaks yang sama, yaitu stok["kunci"] = nilai. Kalau kuncinya belum ada, data baru dibuat. Kalau sudah ada, nilainya ditimpa. Menghapus pakai fungsi bawaan delete.
Comma ok, cara aman mengecek isi map
Sekarang bagian penting. Apa yang terjadi kalau kita membaca kunci yang tidak ada?
fmt.Println(stok["beras"]) // 0
Go tidak error. Ia mengembalikan zero value dari tipe nilainya. Masih ingat materi zero value di bagian 2? Untuk int, zero value adalah 0. Di sinilah masalahnya. Angka 0 itu ambigu. Apakah beras memang tidak terdaftar, atau beras terdaftar tapi stoknya sedang kosong? Dua kondisi yang sangat berbeda, hasilnya sama-sama 0.
Go menyediakan cara untuk membedakannya. Namanya comma ok idiom.
jumlah, ok := stok["beras"]
if ok {
fmt.Println("beras terdaftar, stok:", jumlah)
} else {
fmt.Println("beras tidak terdaftar di toko")
}
Saat membaca map dengan dua variabel penampung, variabel kedua berisi true kalau kuncinya memang ada, dan false kalau tidak. Dengan begitu kita tidak lagi tertipu oleh zero value. Biasakan pakai pola ini setiap kali keberadaan kunci itu penting untuk logika program. Di program latihan nanti, pola ini yang membedakan barang habis dengan barang yang memang tidak dijual.
Map dengan value struct
Sama seperti slice, nilai pada map juga boleh berupa struct. Cocok untuk katalog, di mana nama barang jadi kunci dan detailnya jadi nilai.
katalog := map[string]Produk{
"kopi": {Nama: "Kopi Bubuk", Harga: 25000},
"gula": {Nama: "Gula Pasir", Harga: 15000},
}
fmt.Println(katalog["kopi"].Harga) // 25000
Sekali akses, kita langsung dapat struct utuh lengkap dengan semua field-nya.
Kapan pakai slice, kapan pakai map
Aturan praktisnya sederhana.
- Pakai slice kalau urutan itu penting atau data memang berbentuk antrean. Daftar transaksi, riwayat chat, isi keranjang belanja. Data tersimpan berurutan dan boleh ada isi yang kembar.
- Pakai map kalau Anda sering mencari data berdasarkan kunci tertentu. Stok per nama barang, user per email, konfigurasi per nama setting. Aksesnya langsung ketemu tanpa menelusuri satu per satu, tapi urutannya tidak dijamin.
Satu catatan soal urutan. Saat map ditelusuri dengan range, Go sengaja mengacak urutannya. Jadi jangan pernah mengandalkan urutan pada map. Kalau butuh urutan, pakailah slice. Keduanya juga sering dipakai bersamaan dalam satu program, seperti yang akan kita lakukan di latihan.
Dua jebakan yang sering menjerat pemula
Nil map tidak bisa diisi
Kalau map hanya dideklarasikan tanpa diinisialisasi, nilainya nil. Membaca nil map masih aman, hasilnya zero value. Tapi menulis ke nil map membuat program langsung panic alias berhenti mendadak.
var umur map[string]int
umur["budi"] = 20 // panic: assignment to entry in nil map
Solusinya, siapkan map-nya dulu dengan make atau dengan literal kosong.
umur := make(map[string]int)
umur["budi"] = 20 // aman
Anggap make seperti memasang papan loker sebelum kunci pertama dibagikan. Slice lebih pemaaf di sini, karena append pada slice nil tetap aman. Map tidak. Ini salah satu penyebab panic paling umum pada program Go pemula.
Slice yang berbagi data
Jebakan kedua lebih halus. Hasil pemotongan slice tidak menyalin data. Ia menunjuk ke deretan data yang sama dengan slice asalnya. Mengubah satu, yang lain ikut berubah.
angka := []int{1, 2, 3, 4}
potongan := angka[0:2]
potongan[0] = 99
fmt.Println(potongan) // [99 2]
fmt.Println(angka) // [99 2 3 4]
Perhatikan, angka ikut berubah padahal kita hanya mengubah potongan. Keduanya memandang lemari data yang sama dari jendela yang berbeda. Ini bukan bug, ini memang desain Go supaya pemotongan slice murah dan cepat. Tapi kalau tidak sadar, efek samping seperti ini bisa bikin bingung berjam-jam. Kalau memang butuh salinan yang benar-benar terpisah, Go punya fungsi copy yang bisa Anda eksplorasi sendiri.
Latihan: keranjang belanja mini
Waktunya merangkai semua materi hari ini jadi satu program utuh. Kita buat keranjang belanja mini dengan aturan begini. Stok barang tersimpan di map. Katalog produk tersimpan di map dengan value struct. Isi keranjang tersimpan di slice. Fungsi penambah barang wajib mengecek dulu lewat comma ok, supaya barang yang tidak dijual dan barang yang stoknya habis diperlakukan beda.
package main
import "fmt"
type Produk struct {
Nama string
Harga int
}
func tambahKeKeranjang(keranjang []Produk, stok map[string]int, katalog map[string]Produk, nama string) []Produk {
sisa, ok := stok[nama]
if !ok {
fmt.Println(nama, ": barang tidak ada di toko")
return keranjang
}
if sisa == 0 {
fmt.Println(nama, ": stok sedang habis")
return keranjang
}
stok[nama] = sisa - 1
keranjang = append(keranjang, katalog[nama])
fmt.Println(nama, ": masuk keranjang")
return keranjang
}
func main() {
stok := map[string]int{
"kopi": 2,
"gula": 0,
"teh": 5,
}
katalog := map[string]Produk{
"kopi": {Nama: "Kopi Bubuk", Harga: 25000},
"gula": {Nama: "Gula Pasir", Harga: 15000},
"teh": {Nama: "Teh Celup", Harga: 10000},
}
var keranjang []Produk
keranjang = tambahKeKeranjang(keranjang, stok, katalog, "kopi")
keranjang = tambahKeKeranjang(keranjang, stok, katalog, "teh")
keranjang = tambahKeKeranjang(keranjang, stok, katalog, "gula")
keranjang = tambahKeKeranjang(keranjang, stok, katalog, "beras")
fmt.Println()
fmt.Println("Isi keranjang:")
total := 0
for _, p := range keranjang {
fmt.Println("-", p.Nama, "Rp", p.Harga)
total = total + p.Harga
}
fmt.Println("Total belanja: Rp", total)
}
Jalankan dengan go run main.go. Hasilnya seperti ini.
kopi : masuk keranjang
teh : masuk keranjang
gula : stok sedang habis
beras : barang tidak ada di toko
Isi keranjang:
- Kopi Bubuk Rp 25000
- Teh Celup Rp 10000
Total belanja: Rp 35000
Coba telusuri alurnya pelan-pelan. Kopi dan teh masuk keranjang karena kuncinya ada dan stoknya lebih dari 0. Gula ditolak dengan pesan stok habis, karena comma ok mengembalikan true tapi sisanya 0. Beras ditolak dengan pesan berbeda, karena comma ok mengembalikan false. Tanpa idiom itu, gula dan beras akan terlihat sama persis di mata program. Perhatikan juga bahwa tambahKeKeranjang mengembalikan slice baru dan hasilnya selalu kita tampung lagi, konsisten dengan pola append yang tadi kita bahas.
Sebagai latihan tambahan, coba dua hal ini. Pertama, tambahkan barang baru ke katalog dan stok, lalu beli beberapa kali sampai stoknya habis. Kedua, ubah program supaya satu barang bisa dibeli dengan jumlah tertentu sekaligus, bukan satu-satu.
Selanjutnya di seri ini
Anda sekarang pegang dua wadah data paling penting di Go. Slice untuk daftar yang berurutan, map untuk pencarian cepat lewat kunci, plus comma ok untuk membaca map dengan aman. Sepanjang artikel ini kita sudah beberapa kali memakai for range dan if tanpa membedahnya dalam-dalam. Itu sengaja. Di bagian 6, Perulangan for dan Percabangan: Mengendalikan Alur Program, keduanya kita kupas tuntas, mulai dari berbagai bentuk for sampai switch. Sampai jumpa di bagian berikutnya.
Kalau bisnis Anda butuh aplikasi dengan pengelolaan data seperti ini, mulai dari katalog produk sampai sistem stok, tim kami siap membantu lewat layanan pengembangan sistem aplikasi.
Artikel Lainnya di Kategori Golang
Golang 28 Juli 2026
Belajar Golang dari Nol #2: Variabel, Tipe Data, dan Zero Value
Lanjutan seri Go pemula: tiga cara deklarasi variabel, tipe data dasar, zero value, konstanta, konversi tipe, plus latihan program hitung belanja.
Baca Artikel
Golang 27 Juli 2026
Belajar Golang dari Nol #1: Kenalan dengan Go dan Cara Installnya
Kenalan dengan Go: bahasa apa, siapa pemakainya, cara installnya di Linux, Windows, macOS, sampai program pertama. Panduan pemula tanpa ribet.
Baca Artikel
Golang 30 Juli 2026
Belajar Golang dari Nol #4: Struct dan Method, Cara Go Menyusun Data
Kenalan dengan struct dan method di Go: cara mengelompokkan data, pointer vs value receiver, constructor NewProduk, plus latihan sistem stok mini.
Baca ArtikelIngin Membaca Artikel Lainnya?
Temukan lebih banyak insight dan tips tentang teknologi dan bisnis digital.
Lihat Semua Artikel