Go Map Patterns: From Declaration to Ordered Iteration
Go maps provide built-in associative arrays backed by hash tibles. A map type is written as map[K]V, where the key type K must be comparable and the value type V may be any valid Go type.
The zero value of a declared map is nil. Reads from a nil map behave like an empty map, but writes panic, so storage must be allocated before assignment.
Declaration and allocation
var scores map[string]int // nil; cannot store entries yet
scores = make(map[string]int, 8) // allocates backing storage
Alternatively, use a composite literal:
colors := map[string]string{
"red": "#FF0000",
"green": "#00FF00",
}
Core operations
Insert or update an entry with bracket syntax:
scores["alice"] = 92
scores["bob"] = 87
scores["alice"] = 95 // overwrites previous value
Fetch a value and confirm its presence with the two-result idiom:
grade, ok := scores["alice"]
if ok {
fmt.Println("Grade:", grade)
}
Remove an entry; the operation is safe even when the key does not exist:
delete(scores, "bob")
Iterate over all pairs with range. The iteration order is deliberately randomized:
for pupil, mark := range scores {
fmt.Printf("%s -> %d\n", pupil, mark)
}
Report the number of stored pairs with len:
fmt.Println(len(scores))
Maps are reference types. Assigning one map variable to another causes both to refer to the same underlying data. However, individual map elements are not addressable variables, so taking their address with & is invalid.
Nested maps
A value type may itself be a map, producing nested lookups. Each nested map requires its own initialization:
campus := make(map[string]map[string]string)
campus["building_a"] = make(map[string]string)
campus["building_a"]["room_101"] = "lecture"
campus["building_a"]["room_102"] = "lab"
When writing helper functions that populate nested maps, verify the inner map exists before use:
func enroll(registry map[string]map[string]string, user string) {
if _, exists := registry[user]; !exists {
registry[user] = make(map[string]string)
}
registry[user]["role"] = "member"
registry[user]["status"] = "active"
}
Traverse nested structures with nested range loops:
for outerKey, innerMap := range campus {
fmt.Println(outerKey)
for innerKey, val := range innerMap {
fmt.Printf(" %s: %s\n", innerKey, val)
}
}
Delete from the outer map to remove an entire branch:
delete(campus, "building_a")
Slices containing maps
A slice may store maps as its elements. Because slice entries default to nil, initialize each index before assignment:
rows := make([]map[string]int, 4)
if rows[0] == nil {
rows[0] = make(map[string]int)
}
rows[0]["quantity"] = 12
Sorting by keys
Maps do not maintain insertion or any sorted order. To process entries sequentially, extract the keys, sort them, and then index back into the map:
timings := map[int]int{
800: 25,
1200: 40,
930: 15,
}
slots := make([]int, 0, len(timings))
for k := range timings {
slots = append(slots, k)
}
sort.Ints(slots)
for _, k := range slots {
fmt.Println(k, timings[k])
}
Inverting keys and values
You can build a reverse-lookup map by swapping positions. This is only well-defined when the original values are unique and comparable:
labels := map[string]int{
"error": 1,
"warning": 2,
"info": 3,
}
codes := make(map[int]string, len(labels))
for name, code := range labels {
codes[code] = name
}