23. Map
Map menghubungkan kunci dengan nilai. Slice mencari nilai berdasarkan posisinya (0, 1, 2). Map mencarinya berdasarkan kunci, seperti saat kamu mencari kata di kamus.
Membuat map
Literal map adalah tipenya, diikuti pasangan
kunci: nilai di dalam kurung kurawal:
stats := map[string]int{
"level": 7,
"hp": 95,
"coins": 12,
}Baca map[string]int sebagai "map dari string ke int".
Semua kuncinya string, dan semua nilainya int. Kalau ditulis dalam
beberapa baris, setiap pasangan butuh koma di belakangnya, termasuk yang
terakhir. Tanpa koma terakhir itu, Go berhenti:
syntax error: unexpected newline in composite literal; possibly missing comma or }
Baca nilai dengan kurung siku:
stats := map[string]int{
"level": 7,
"hp": 95,
"coins": 12,
}
fmt.Println(stats["level"]) // 7
fmt.Println(stats["hp"]) // 95Satu map punya satu tipe nilai. Ini batasan nyata di Go, dan membentuk sisa bab ini. Nama adalah string dan level adalah int, jadi keduanya tidak muat dalam map yang sama:
player := map[string]int{
"name": "Keiko",
"level": 7,
}
fmt.Println(player)cannot use "Keiko" (untyped string constant) as int value in map literal
Untuk sekarang, simpan nilai dengan tipe berbeda di variabel atau map yang berbeda. Bab 26 memperkenalkan struct, yang menyatukan nilai dengan tipe berbeda.
Untuk memulai dengan map kosong dan mengisinya nanti, pakai
make, yang kamu temui di Bab 22. len
memberikan jumlah kunci:
stats := make(map[string]int)
stats["level"] = 1
fmt.Println(len(stats)) // 1Map tidak punya penulisan dengan titik. stats.level
tidak bisa dikompilasi, dan kamu selalu menulis
stats["level"]:
stats.level undefined (type map[string]int has no field or method level)
Kalau kuncinya disimpan di variabel, itu juga bisa:
stats := map[string]int{"level": 7, "hp": 95}
field := "hp"
fmt.Println(stats[field]) // 95Menambah dan mengubah nilai
Menugaskan ke kunci baru akan membuatnya. Menugaskan ke kunci yang
sudah ada akan menggantinya. delete menghapusnya:
stats := map[string]int{"level": 7, "hp": 95, "coins": 12}
stats["mana"] = 30 // new key
stats["hp"] = 100 // replace existing
delete(stats, "coins")
fmt.Println(len(stats)) // 3Setelah tiga baris itu, stats berisi
"level", "hp", dan "mana".
Menghapus kunci yang tidak ada tidak melakukan apa-apa. Contoh-contoh
ini ada di exercises/23/01-map.go.
Perulangan dengan
range
range memberimu setiap kunci dan nilai di dalam map:
for key, value := range stats {
fmt.Println(key, value)
}Map tidak punya urutan. Go mengunjungi kunci dalam
urutan acak, dan urutannya berubah dari satu perulangan ke perulangan
berikutnya. exercises/23/04-random-order.go mengulang map
yang sama tiga kali, lalu mencetak nama yang sudah diurutkan. Tiga baris
pertama pada output berikut berasal dari satu kali jalan yang
sebenarnya, dan punyamu akan berbeda:
Ada Ben Cara Dan Eve
Eve Ada Ben Cara Dan
Dan Eve Ada Ben Cara
[Ada Ben Cara Dan Eve]
Jangan pernah menulis program yang outputnya bergantung pada urutan ini.
Kalau kamu butuh urutan yang tetap, kumpulkan kuncinya ke dalam
slice, urutkan, lalu ulangi slice itu. slices.Sort, dari
package slices, mengurutkan slice di tempat. Bab 24
membahas package itu. Tambahkan "slices" ke import-mu:
stats := map[string]int{"level": 7, "hp": 100, "mana": 30}
keys := []string{}
for key := range stats {
keys = append(keys, key)
}
slices.Sort(keys)
for _, key := range keys {
fmt.Println(key, stats[key])
}Output, yang sama di setiap kali jalan:
hp 100
level 7
mana 30
Untuk melihat seluruh map dengan cepat, fmt.Println
mencetaknya dengan kunci terurut. Ini hanya berlaku saat mencetak, bukan
pada range:
stats := map[string]int{"level": 7, "hp": 100, "mana": 30}
fmt.Println(stats) // map[hp:100 level:7 mana:30]Aman untuk memakai delete pada kunci yang sedang
dikunjungi di dalam perulangan range.
Memeriksa apakah kunci ada
Meminta kunci yang tidak ada tidak membuat crash. Hasilnya adalah
nilai nol dari tipe nilainya, yaitu 0
untuk int dan "" untuk string. Jadi kunci yang tidak ada
bisa terlihat persis seperti 0 sungguhan:
stats := map[string]int{"level": 7, "hp": 0}
fmt.Println(stats["hp"]) // 0
fmt.Println(stats["email"]) // 0Ini masalah yang sama dengan teks yang salah dan
strconv.Atoi di Bab 21, dan jawabannya punya bentuk yang
sama. Minta dua nilai. Yang kedua adalah bool, biasanya bernama
ok, yang menyatakan apakah kuncinya ada:
stats := map[string]int{"level": 7, "hp": 0}
hp, ok := stats["hp"]
fmt.Println(hp, ok) // 0 true
mana, ok := stats["mana"]
fmt.Println(mana, ok) // 0 falsePakai bersama if:
stats := map[string]int{"level": 7, "hp": 0}
coins, ok := stats["coins"]
if ok {
fmt.Println("Coins:", coins)
} else {
fmt.Println("No coins on file.")
}Output:
No coins on file.
Map yang dideklarasikan dengan var tanpa nilai, seperti
var stats map[string]int, belum siap dipakai. Membacanya
memberikan nilai nol, tetapi menugaskan ke dalamnya membuat crash dengan
panic: assignment to entry in nil map. Buat map dengan
literal atau make, dan hal ini tidak akan terjadi.
Map dibagi kalau kamu memberikannya ke fungsi, sama seperti slice. Fungsi yang mengisi sebuah kunci juga mengisinya untuk pemanggil, tanpa perlu mengembalikan apa-apa:
func addCoins(stats map[string]int, amount int) {
stats["coins"] += amount
}
func main() {
stats := map[string]int{"coins": 5}
addCoins(stats, 10)
fmt.Println(stats["coins"]) // 15
}Map bersarang
Nilai map bisa berupa map lain, atau slice. Tipenya menyatakan hal itu:
positions := map[string]map[string]int{
"Keiko": {"x": 0, "y": 5, "z": -3},
}
inventories := map[string][]string{
"Keiko": {"sword", "potion", "map"},
}
fmt.Println(positions["Keiko"]["x"]) // 0
fmt.Println(inventories["Keiko"][0]) // swordmap[string]map[string]int dibaca "map dari string ke map
dari string ke int". Di dalam literal, Go mengizinkan kamu tidak menulis
tipe bagian dalam, jadi {"x": 0, ...} sudah cukup.
Untuk memanjangkan slice yang disimpan di map, tugaskan hasil
append kembali ke kunci itu:
inventories["Keiko"] = append(inventories["Keiko"], "rope")Contoh-contoh ini ada di exercises/23/02-nested.go.
Map dan slice bersama-sama
Sebuah quest punya nama, hadiah, dan daftar langkah. Itu tiga tipe yang berbeda, dan satu map tidak bisa menampungnya. Cara yang bisa dipakai pada tahap ini adalah dua map dengan kunci yang sama, satu map untuk setiap jenis informasi:
questSteps := map[string][]string{
"Healer's Errand": {
"Speak to the elder",
"Collect 5 herbs",
"Return to camp",
},
}
questRewards := map[string]int{
"Healer's Errand": 50,
}
name := "Healer's Errand"
fmt.Println(questRewards[name]) // 50
fmt.Println(questSteps[name][0]) // Speak to the elder
fmt.Println(len(questSteps[name])) // 3Ini adalah exercises/23/03-mixed.go. Mini-proyek Bagian
5 membangun seluruh dunia game-nya dengan cara ini. Cara ini berjalan,
tetapi tidak ada yang menjaga kedua map tetap selaras. Kalau kamu
menambahkan quest ke satu map dan lupa pada yang lain, Go tidak akan
memberi tahu. Bab 26 memperbaikinya.
PR
Starter PR ada di exercises/23/homework/. Setiap starter
bisa langsung dijalankan dan mencetak satu baris pengganti.
Soal 1 — Catatan pemain
Buka exercises/23/homework/01-player-record.go. Buat
map[string]string bernama player dengan
setidaknya empat kunci ini: "name", "class",
"level", "alive". Semua nilainya berupa teks,
seperti "7" dan "true", karena satu map punya
satu tipe nilai. Lalu cetak setiap kunci dan nilai, satu per baris,
dengan urutan kunci yang terurut.
Soal 2 — Berat inventaris
Buka exercises/23/homework/02-inventory-weights.go.
Sebuah map menghubungkan nama barang dengan beratnya. Hitung dan cetak
total berat dengan dua desimal.
Soal 3 — Pencarian aman
Buka exercises/23/homework/03-safe-lookup.go. Tulis
fungsi lookup(player map[string]string, key string) string
yang mengembalikan nilainya kalau kuncinya ada, dan string
"(not found)" kalau tidak. Uji dengan tiga panggilan: satu
yang ketemu, satu yang tidak ketemu, dan satu yang kuncinya disimpan di
variabel.
Tantangan — Frekuensi kata
Buka exercises/23/homework/04-word-frequency.go. Dari
slice berisi kata (sebagian berulang), buat map dari setiap kata unik ke
jumlahnya. Cetak satu pasangan per baris, dengan urutan kata yang
terurut.
Stuck atau sudah selesai? Buka halaman solusi PR.