Beginner · Collections and structs · topic 14 of 46 · 35 min
Maps: key-value look-ups
Explanation
Why maps?
With a slice you find items by position. Often you want to find them by a name instead: the price of "chai", the marks of roll number 23, the city for PIN code 411001. A map is a collection of key-value pairs where you look up a value by its key, very fast, even with millions of entries.
Creating and using a map
prices := map[string]int{
"chai": 15,
"samosa": 20,
}
prices["vada pav"] = 25 // add
prices["chai"] = 18 // update
delete(prices, "samosa") // remove
fmt.Println(prices["chai"], len(prices)) // 18 2
fmt.Println(prices) // map[chai:18 vada pav:25]counts := make(map[string]int) // an empty map, ready to useMissing keys and "comma ok"
Reading a key that is not in the map does not crash. It gives the zero value of the value type: 0, "", false. That is handy for counting, but it means you cannot tell "price is 0" from "item not on the menu". The comma ok form answers that.
price, ok := prices["dosa"]
if !ok {
fmt.Println("dosa is not on the menu")
} else {
fmt.Println("dosa costs", price)
}
if p, ok := prices["chai"]; ok {
fmt.Println("chai:", p)
}Counting with a map
words := strings.Fields("chai samosa chai chai vada")
count := map[string]int{}
for _, w := range words {
count[w]++ // missing keys start at 0
}
fmt.Println(count) // map[chai:3 samosa:1 vada:1]Iteration order is random
for k, v := range m visits every pair, but the order is not specified and Go deliberately changes it between runs, so nobody relies on it. When you need a stable order (for a report or a test), collect the keys, sort them, then loop over the sorted keys.
keys := make([]string, 0, len(count))
for k := range count {
keys = append(keys, k)
}
slices.Sort(keys)
for _, k := range keys {
fmt.Println(k, count[k])
}
// Go 1.23+: keys := slices.Sorted(maps.Keys(count))Three rules that cause crashes
- A nil map can be read but not written. var m map[string]int; m["a"] = 1 panics with "assignment to entry in nil map". Always create maps with make or a literal.
- Maps are not safe for concurrent use. If one goroutine writes while another reads or writes, Go may stop the program with "fatal error: concurrent map writes", which cannot be recovered. Protect the map with a sync.Mutex (module 6) or use sync.Map for special cases.
- Keys must be comparable with ==. Strings, numbers, bools, arrays and structs of those work; slices, maps and functions cannot be keys.
Maps share data
Like slices, a map value is a small reference to the real data. Passing a map to a function and changing it inside changes the caller's map too. Copy it (maps.Clone in Go 1.21+) if you need a separate version.
Maps of slices and of structs
byCity := map[string][]string{}
byCity["Pune"] = append(byCity["Pune"], "Asha") // nil slice + append works
byCity["Pune"] = append(byCity["Pune"], "Ravi")
fmt.Println(byCity) // map[Pune:[Asha Ravi]]
type Stock struct{ Qty int }
stock := map[string]Stock{"rice": {Qty: 10}}
// stock["rice"].Qty-- // compile error: cannot assign to struct field in map
s := stock["rice"]
s.Qty--
stock["rice"] = s // read, change, write backNeed help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Maps: key-value look-ups · 1/7
A map looks up a value by its key, like a phone book or a price list.
Prefer a video by a teacher? Find free videos on “golang maps tutorial comma ok idiom iteration order” on YouTube ↗
In real life: PIN code to city look-up
A delivery app checks if it serves a customer's PIN code before showing the checkout button. A map from PIN code to city answers instantly. The comma ok form separates "not served" from a real entry, and the app shows "Sorry, we do not deliver here yet" for unknown codes.
package main
import "fmt"
func main() {
served := map[string]string{
"411001": "Pune",
"560001": "Bengaluru",
"110001": "New Delhi",
}
for _, pin := range []string{"560001", "999999"} {
if city, ok := served[pin]; ok {
fmt.Println(pin, "→ we deliver in", city)
} else {
fmt.Println(pin, "→ sorry, not served yet")
}
}
}Practice (0/4 done)
What does this print? m := map[string]int{"a": 1} fmt.Println(m["b"])
What does this print? m := map[string]int{"b": 2, "a": 1} fmt.Println(m)
Which line panics? 1: var m map[string]int 2: fmt.Println(m["x"]) 3: m["x"] = 1
Given a slice of UPI transactions as category strings (for example "food", "travel", "food", "bills"), count how many times each category appears and print the categories in alphabetical order with their counts.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Structs and methodsCollections and structs · same module
- Go language interview questionsCareers and interviews
- Pointers without fearPointers, interfaces and errors
- std::map and std::unordered_mapC++ Programming (modern C++ and DSA) · The Standard Template Library (STL)
- panic and recover: only for the truly unexpectedPointers, interfaces and errors
- any, type assertions and type switchesPointers, interfaces and errors
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does v, ok := m[k] give when k is missing?
Answer: zero value and false
ok is false and v is the zero value of the value type.
Which cannot be a map key?
Answer: []int
Slices are not comparable, so they cannot be keys.
Is the order of for k := range m the same every run?
Answer: No, it is unspecified and randomised
Never rely on map iteration order.
Two goroutines write to the same map without a lock. What can happen?
Answer: Go may crash with "fatal error: concurrent map writes"
Unprotected concurrent map writes are a fatal run-time error. Use a mutex.
How do you remove key "a" from map m?
Answer: delete(m, "a")
The built-in delete function removes a key. Deleting a missing key does nothing.
How does Go provide polymorphism without inheritance?Related
Answer: Interfaces satisfied implicitly
Any type with the right methods can be used through an interface.
What is the zero value of a pointer?Related
What is the time to find a key in std::map with n keys?Related
Answer: O(log n)
std::map is a balanced tree, so its height is about log n.
Which three things make up a slice header?Related
Answer: Pointer to array, length, capacity
A slice is a pointer, a length and a capacity.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Collections and structs” before its exam: Structs and methods.
- Module 3 exam: Collections and structs · 15 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Pointers, interfaces and errors. 46 topics left in the course before the final exam.