Learn Go
beginner1 min read

Maps

A map is Go's built-in associative data structure — an unordered collection of key–value pairs. Maps are implemented as hash tables and provide O(1) average-case lookups, insertions, and deletions.

Creating Maps

Use a map literal or make:

package main
 
import "fmt"
 
func main() {
    // Map literal
    capitals := map[string]string{
        "France":  "Paris",
        "Germany": "Berlin",
        "Japan":   "Tokyo",
    }
    fmt.Println(capitals["France"]) // Paris
 
    // make
    scores := make(map[string]int)
    scores["Alice"] = 100
    scores["Bob"]   = 85
    fmt.Println(scores) // map[Alice:100 Bob:85]
}

CRUD Operations

package main
 
import "fmt"
 
func main() {
    stock := map[string]int{
        "apples":  50,
        "bananas": 30,
    }
 
    // Read
    fmt.Println(stock["apples"]) // 50
 
    // Update
    stock["apples"] = 45
 
    // Create
    stock["cherries"] = 100
 
    // Delete
    delete(stock, "bananas")
 
    fmt.Println(stock) // map[apples:45 cherries:100]
}

Checking Whether a Key Exists

A map lookup returns two values: the value and a boolean indicating whether the key was present. This is the idiomatic pattern:

package main
 
import "fmt"
 
func main() {
    ages := map[string]int{
        "Alice": 30,
        "Bob":   25,
    }
 
    if age, ok := ages["Alice"]; ok {
        fmt.Println("Alice's age:", age)
    }
 
    if _, ok := ages["Charlie"]; !ok {
        fmt.Println("Charlie not found")
    }
 
    // Without ok: missing keys return the zero value
    fmt.Println(ages["Charlie"]) // 0 — no error, but misleading
}

Idiomatic Go: Always use the two-value form val, ok := m[key] when the key's presence is meaningful. Relying on the zero value can hide bugs.

Nil Maps

A var declaration without initialisation produces a nil map. Reading from a nil map returns the zero value; writing to one panics:

package main
 
import "fmt"
 
func main() {
    var m map[string]int
 
    fmt.Println(m["key"]) // 0 — safe to read
    fmt.Println(m == nil) // true
 
    // m["key"] = 1 // would panic: assignment to entry in nil map
    m = make(map[string]int)
    m["key"] = 1
    fmt.Println(m["key"]) // 1
}

Iterating with range

Map iteration order is deliberately randomised in Go. Do not rely on it:

package main
 
import (
    "fmt"
    "sort"
)
 
func main() {
    population := map[string]int{
        "London":  9_000_000,
        "Tokyo":   14_000_000,
        "New York": 8_000_000,
    }
 
    // Collect and sort keys for deterministic output
    keys := make([]string, 0, len(population))
    for k := range population {
        keys = append(keys, k)
    }
    sort.Strings(keys)
 
    for _, k := range keys {
        fmt.Printf("%s: %d\n", k, population[k])
    }
}

Maps of Slices

A common pattern is a map where each value is a slice — useful for grouping:

package main
 
import "fmt"
 
func groupByLength(words []string) map[int][]string {
    groups := make(map[int][]string)
    for _, w := range words {
        groups[len(w)] = append(groups[len(w)], w)
    }
    return groups
}
 
func main() {
    words := []string{"go", "is", "fun", "and", "fast"}
    groups := groupByLength(words)
    fmt.Println(groups[2]) // [go is]
    fmt.Println(groups[3]) // [fun and]
    fmt.Println(groups[4]) // [fast]
}

Key Takeaways