Go Maps

A map in Go is a built-in reference type that stores key-value pairs, giving you fast, dynamic lookups when you don't know your data's keys ahead of time.

Declaring and Creating Maps

Maps are written as map[KeyType]ValueType. A nil map (the zero value) can be read from but panics if you try to write to it, so in practice you almost always create maps with the built-in make function or a map literal before storing anything in them.

Creating a map with make and a literal

package main

import "fmt"

func main() {
	// Using make - starts empty
	ages := make(map[string]int)
	ages["Nia"] = 24
	ages["Tom"] = 31

	// Using a map literal - pre-filled
	capitals := map[string]string{
		"France": "Paris",
		"Japan":  "Tokyo",
	}

	fmt.Println(ages)
	fmt.Println(capitals)
}
Note: var m map[string]int declares a nil map. Reading m["x"] safely returns 0, but m["x"] = 5 will panic with 'assignment to entry in nil map'. Always initialize with make or a literal before writing.

Reading Keys Safely with the Comma-ok Idiom

Looking up a missing key does not panic; it simply returns the value type's zero value, which can hide bugs if zero is also a valid value in your data. Go solves this with a special two-value form of the lookup that also tells you whether the key was actually present.

Comma-ok lookup

package main

import "fmt"

func main() {
	scores := map[string]int{"Ada": 95, "Lin": 88}

	value, ok := scores["Ada"]
	fmt.Println(value, ok) // 95 true

	value, ok = scores["Zed"]
	fmt.Println(value, ok) // 0 false

	if v, found := scores["Lin"]; found {
		fmt.Println("Lin scored", v)
	}
}

Deleting Keys and Iterating

The built-in delete function removes a key-value pair from a map; calling it on a key that doesn't exist is a harmless no-op. Iterating over a map with range visits every key-value pair, but Go deliberately randomizes the order on each run so you never accidentally depend on a fixed sequence.

Deleting keys and ranging over a map

package main

import "fmt"

func main() {
	inventory := map[string]int{"apples": 10, "pears": 4, "limes": 0}

	delete(inventory, "limes")

	total := 0
	for item, qty := range inventory {
		fmt.Println(item, "->", qty)
		total += qty
	}
	fmt.Println("total stock:", total)
}
  • Always create maps with make(...) or a literal before writing to them.
  • Use the comma-ok form when a missing key and a zero value need to mean different things.
  • delete(m, key) is safe to call even if the key isn't present.
  • Map iteration order is intentionally random - sort keys yourself if you need a stable order.
  • len(m) returns the number of key-value pairs currently stored in the map.

Maps vs Slices at a Glance

AspectMapSlice
Access byKey (any comparable type)Integer index
Ordered?No, unorderedYes, preserves insertion order
Zero value usable?Readable, not writablenil slice is both readable and appendable
Typical useLookups, counting, groupingOrdered lists, sequences
Note: A common pattern is using a map[string]bool or the empty struct map[string]struct{} as a lightweight set, since Go has no dedicated set type in its standard library.

Exercise: Go Maps

Is the iteration order of a Go map's keys guaranteed?