GO

Go Fundamentals

18 lessons

Progress0%
1. Introduction to Go
1What is Go?
2. Variables and Data Types
1Data Types in Go
3. Control Flow
If, For, and SwitchDefer, Panic, Recover
4. Functions
Function BasicsError Handling
5. Structs and Methods
StructsMethods and Interfaces
6. Concurrency
Goroutines and ChannelsSelect and Sync
7. Maps & Slices Advanced
Slices Deep DiveMaps Operations & Patterns
8. Interfaces Deep Dive
Interface Composition & anyCommon Interfaces & Patterns
9. Packages & Modules
Package SystemGo Modules & Workspace
10. Testing & Standard Library
Testing in GoStandard Library Essentials
All Tutorials
GoMaps & Slices Advanced
Lesson 12 of 18 min
Chapter 7 · Lesson 2

Maps Operations & Patterns

Maps Operations & Patterns

A map in Go is an unordered collection of key-value pairs. Maps are reference types — assigning a map to another variable or passing it to a function shares the underlying data.

Creating Maps

go
// With make — preferred when size is unknown
m := make(map[string]int)

// Map literal — preferred when values are known upfront
m := map[string]int{"a": 1, "b": 2}

Reading, Writing and Deleting

go
m["key"] = 42      // write / update
v := m["key"]      // read (returns zero value if key absent)
delete(m, "key")   // remove entry (no-op if key absent)

Checking Key Existence — the comma-ok idiom A plain read returns the zero value for missing keys, which is ambiguous. Use the two-value form to distinguish:

go
v, ok := m["key"]
if ok {
    fmt.Println("found:", v)
} else {
    fmt.Println("not found")
}

Iterating with range Map iteration order is intentionally randomised on every run:

go
for k, v := range m {
    fmt.Println(k, v)
}

Maps of Slices Group values under a key by storing a slice as the value type:

go
groups := make(map[string][]string)
groups["fruit"] = append(groups["fruit"], "apple")

This is safe because appending to a nil slice is valid.

Word Count Pattern A classic use-case — count occurrences of each word:

go
counts := make(map[string]int)
for _, word := range words {
    counts[word]++
}

The expression counts[word]++ leverages the zero-value default: a missing key returns 0, then increments to 1.

Sorting Map Keys for Deterministic Output When you need predictable output (tests, reports), extract and sort the keys:

go
keys := make([]string, 0, len(m))
for k := range m {
    keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
    fmt.Println(k, m[k])
}

nil Map vs Empty Map

  • var m map[string]int — nil map; reads return zero values safely, but writes panic
  • m := make(map[string]int) — empty map; reads and writes are both safe

Always initialise a map before writing to it. A common guard pattern:

go
if m == nil {
    m = make(map[string]int)
}

Maps cannot hold maps as values that reference themselves (no cycles), and map keys must be comparable types (no slices, maps, or functions as keys).

Code Examples

Word count with a mapgo
package main

import (
    "fmt"
    "sort"
    "strings"
)

func wordCount(text string) map[string]int {
    counts := make(map[string]int)
    words := strings.Fields(text) // splits on any whitespace
    for _, w := range words {
        w = strings.ToLower(w)
        w = strings.Trim(w, ".,!?;:")
        counts[w]++
    }
    return counts
}

func main() {
    text := "go is fast, go is simple, and go is fun!"
    counts := wordCount(text)

    // Sort keys for deterministic output
    keys := make([]string, 0, len(counts))
    for k := range counts {
        keys = append(keys, k)
    }
    sort.Strings(keys)

    for _, k := range keys {
        fmt.Printf("%-10s %d\n", k, counts[k])
    }

    // comma-ok to check existence
    if n, ok := counts["go"]; ok {
        fmt.Println("\n'go' appears", n, "times")
    }
}

counts[w]++ exploits the zero-value default. Sorting keys after iteration gives deterministic output regardless of Go's randomised map ordering.

Map of slices — grouping recordsgo
package main

import (
    "fmt"
    "sort"
)

type Employee struct {
    Name       string
    Department string
}

func groupByDept(employees []Employee) map[string][]string {
    groups := make(map[string][]string)
    for _, e := range employees {
        groups[e.Department] = append(groups[e.Department], e.Name)
    }
    return groups
}

func main() {
    staff := []Employee{
        {"Alice", "Engineering"},
        {"Bob", "Marketing"},
        {"Carol", "Engineering"},
        {"Dave", "Marketing"},
        {"Eve", "Engineering"},
    }

    byDept := groupByDept(staff)

    // Print in sorted department order
    depts := make([]string, 0, len(byDept))
    for d := range byDept {
        depts = append(depts, d)
    }
    sort.Strings(depts)

    for _, dept := range depts {
        fmt.Printf("%s: %v\n", dept, byDept[dept])
    }

    // Delete a department entry
    delete(byDept, "Marketing")
    fmt.Println("\nAfter deleting Marketing:", byDept)
}

Appending to a nil slice value in a map is valid; Go returns the zero value (nil slice) first, then append creates the backing array. delete removes the key entirely.

Nil map trap and safe initialisationgo
package main

import "fmt"

func safeMerge(dst, src map[string]int) map[string]int {
    if dst == nil {
        dst = make(map[string]int)
    }
    for k, v := range src {
        dst[k] += v
    }
    return dst
}

func main() {
    // nil map — reads are safe, writes panic
    var scores map[string]int
    fmt.Println("nil map read:", scores["alice"]) // 0, no panic

    // This would panic:
    // scores["alice"] = 10

    // Safe initialisation before writing
    scores = make(map[string]int)
    scores["alice"] = 10
    scores["bob"] = 20

    extra := map[string]int{"alice": 5, "carol": 15}
    merged := safeMerge(scores, extra)
    fmt.Println("merged:", merged)

    // Map is a reference type
    ref := merged
    ref["alice"] = 999
    fmt.Println("merged after ref mutation:", merged["alice"]) // 999
}

Reading a nil map is safe and returns zero values. Writing to a nil map panics at runtime — always initialise with make. Because maps are reference types, assigning to ref also mutates merged.

Quick Quiz

1. What does `v, ok := m["key"]` do when the key is absent?

2. Why is iterating over a map with range non-deterministic in Go?

3. What happens when you write to a nil map?

4. Which types CANNOT be used as map keys in Go?

Was this lesson helpful?

PreviousNext