Chapter 47: Go Maps
1. What is a Map? (The Simplest Explanation)
A map is:
An unordered collection of key–value pairs You give it a key, it instantly gives you back the value (very fast lookup — average O(1))
Think of it as:
- A dictionary (Python)
- A hash map / HashMap (Java)
- An object / associative array (JavaScript)
- A phone book: name (key) → phone number (value)
In Go, maps are reference types (like slices, channels, functions) → they are nil by default and you must make or initialize them before using.
2. Creating a Map – All Realistic Ways
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package main import "fmt" func main() { // Style 1 – make() – most common & recommended scores := make(map[string]int) // key = string, value = int // now safe to write scores["Webliance"] = 95 // Style 2 – literal (short & readable when you know some values) population := map[string]int{ "Hyderabad": 10000000, "Bangalore": 13000000, "Chennai": 11000000, } // Style 3 – zero value / nil map (cannot write to it yet) var emptyMap map[string]int // == nil // emptyMap["key"] = 1 // PANIC: assignment to entry in nil map // Style 4 – make with initial capacity hint (performance optimization) largeMap := make(map[string]int, 1000) // hint: expect ~1000 entries fmt.Printf("scores: %v (len=%d)\n", scores, len(scores)) fmt.Printf("population: %v (len=%d)\n", population, len(population)) fmt.Printf("emptyMap: %v (nil? %t)\n", emptyMap, emptyMap == nil) } |
Golden rule #1:
Never write to a nil map — always create it first with make() or literal.
3. Reading, Writing, Checking Existence, Deleting
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
m := make(map[string]int) m["apple"] = 5 // write / insert / update m["banana"] = 3 value := m["apple"] // read – if key missing → zero value (0 here) fmt.Println("apple:", value) // 5 missing := m["orange"] // key doesn't exist → returns zero value fmt.Println("orange:", missing) // 0 // Correct way to check existence count, exists := m["orange"] if exists { fmt.Println("orange count:", count) } else { fmt.Println("orange not found") // this prints } // Delete a key delete(m, "banana") fmt.Println("After delete →", m) // map[apple:5] |
Very important idiom:
|
0 1 2 3 4 5 6 7 8 9 10 |
if count, ok := m["key"]; ok { // key exists → use count } else { // key missing } |
This is the standard way to safely read from a map in Go.
4. Iterating Over a Map (for range)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
population := map[string]int{ "Hyderabad": 10000000, "Bangalore": 13000000, "Chennai": 11000000, } for city, pop := range population { fmt.Printf("%s has %d people\n", city, pop) } |
Important notes:
- Order is random / undefined — every run may give different order
- If you need sorted order → collect keys → sort them → loop over sorted keys
- range gives copy of key & value — safe to modify value but not key
5. Common Real-World Patterns (You Will Use These Every Day)
Pattern 1: Counting occurrences
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
words := []string{"go", "is", "fun", "go", "is", "powerful"} count := make(map[string]int) for _, w := range words { count[w]++ } fmt.Println(count) // map[go:2 is:2 fun:1 powerful:1] |
Pattern 2: Fast lookup / existence check
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
allowed := map[string]bool{ "admin": true, "editor": true, "viewer": false, } userRole := "admin" if allowed[userRole] { fmt.Println("Access granted") } |
Pattern 3: Grouping data
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
type Person struct { Name string City string } people := []Person{ {Name: "Webliance", City: "Hyderabad"}, {Name: "Ananya", City: "Bangalore"}, {Name: "Rahul", City: "Hyderabad"}, } byCity := make(map[string][]Person) for _, p := range people { byCity[p.City] = append(byCity[p.City], p) } fmt.Println("Hyderabad people:", byCity["Hyderabad"]) |
6. Important Rules & Gotchas
- Nil map panic — most common beginner mistake → always initialize before writing
- Maps are reference types → passing map to function = cheap header copy → modifications affect original
- Keys must be comparable → allowed: int, string, bool, float, struct/array with comparable fields → not allowed: slice, map, function
- No key of type interface{} (unless you use concrete type under interface)
- Maps are not safe for concurrent writes → use sync.Mutex or sync.Map for concurrent access
7. Quick Practice – Try These
- Create a map[string]int that counts how many times each letter appears in “Hyderabad”
- Create map[string][]string → group friends by city
- Write a function that returns true if a key exists in a map
- Write a function that deletes all keys whose value < 10
Which pattern felt most useful for real programs?
Any part still confusing?
- Why maps are unordered / random iteration?
- How to make a map thread-safe?
- Difference between nil map and empty map?
- Or ready to move to methods (functions attached to structs) next?
Keep creating and looping over small maps — they are one of the most powerful tools for fast lookups and grouping in Go.
You’re progressing really fast — keep asking! 💪🇮🇳🚀
