Maps in Go

By Syncfusion®, Inc. · Last updated

Go maps: key-value pairs. Create them with literals and make, add, update, delete and look up values, check keys with comma-ok, loop with range, and use zero values and nested maps.

What Are Maps?

Map = a collection of key and value pairs, where you find a value by its key.

  • Key = the label you look up, like a student's name.
  • Value = what is stored for that key, like their marks.
  • Every key is unique. One key has one value.

Simple lesson: a map stores values by name, so you can find any value straight away.

A Real-World Example

Think of the school's phone directory.

  • Each line has a name and a phone number.
  • To call the principal, you look for "Principal", not for line number 17.
  • Each name appears only once.
  • New names can be added, old ones changed or removed.

Now the mapping:

  • The directory = the map.
  • The name = the key.
  • The phone number = the value.
  • Looking up a name = directory["Principal"].

Slices find values by position: marks[2].

Maps find values by key: marks["Asha"].

Use a map when you think "look up X by its name".

What You Will Learn

In this lesson, you will learn:

  1. What maps and key-value pairs are.
  2. How to create maps with a literal or with make().
  3. How to add, update, delete and look up values.
  4. How to check if a key exists with the comma-ok idiom.
  5. How to loop over a map with range.
  6. Map best practices: zero values and nested maps.

Map Fundamentals

Key-Value Pairs

A map type is written map[KeyType]ValueType.

  • map[string]int = keys are strings, values are ints.
  • map[int]string = keys are ints, values are strings.
  • Keys must be comparable with ==: strings, numbers, bools, arrays and simple structs work. Slices and maps cannot be keys.

Creating Maps

Ways to make a map

Create maps

package main

import "fmt"

func main() {
	marks := map[string]int{
		"Asha": 91,
		"Ravi": 78,
	}
	rooms := make(map[string]string)
	rooms["8A"] = "Room 12"
	fmt.Println(marks)
	fmt.Println(rooms, len(rooms))
}
map[Asha:91 Ravi:78]
map[8A:Room 12] 1
  • fmt.Println prints a map with its keys sorted, so the output is tidy.
  • len(m) gives the number of pairs.

make()

make = build a ready-to-use map (or slice, or channel).

  • make(map[string]int) = an empty map.
  • make(map[string]int, 100) = an empty map with room for about 100 pairs. It still grows if needed.

Simple lesson: write map[K]V{...} or make(map[K]V), then use keys to reach values.

Map Operations

Type a key and a value, then try every button.

Try these:

  • Set a new name: a new pair is added.
  • Set an existing name: its value is replaced.
  • Look up a missing name: you get 0 and ok = false.
  • Delete a missing name: nothing happens, and there is no error.

Add and Update Elements

The same line adds or updates: m[key] = value.

Add and update

package main

import "fmt"

func main() {
	stock := map[string]int{"pencils": 40}
	stock["erasers"] = 25
	stock["pencils"] = 35
	stock["pencils"] += 10
	fmt.Println(stock)
}
map[erasers:25 pencils:45]
  • "erasers" was new, so it is added.
  • "pencils" existed, so it is updated.
  • += works too: it reads, adds, then stores.

Lookup Values

Read values

package main

import "fmt"

func main() {
	marks := map[string]int{"Asha": 91, "Ravi": 78}
	fmt.Println(marks["Asha"])
	fmt.Println(marks["Zoya"])
}
91
0
  • A missing key gives the zero value of the value type: 0 here.
  • There is no error. So 0 could mean "scored 0" or "not in the map".

Delete Elements

delete() = remove a key and its value.

delete()

package main

import "fmt"

func main() {
	club := map[string]string{"Asha": "8A", "Ravi": "8B", "Meena": "8A"}
	delete(club, "Ravi")
	delete(club, "Nobody")
	fmt.Println(club, len(club))
}
map[Asha:8A Meena:8A] 2
  • delete(map, key) removes the pair.
  • Deleting a key that is not there is safe. Nothing happens.

Simple lesson: m[k] = v adds or updates, m[k] reads, and delete(m, k) removes.

Map Lookup

Comma-Ok Idiom

Comma-ok idiom = value, ok := m[key]. ok is true only if the key is really in the map.

Press Play to see both cases.

Is the student in the map?

Checking Key Existence

Check before you use

package main

import "fmt"

func main() {
	rooms := map[string]string{"8A": "Room 12", "8B": "Room 14"}
	if room, ok := rooms["8C"]; ok {
		fmt.Println("8C is in", room)
	} else {
		fmt.Println("No room set for 8C")
	}
	_, has8A := rooms["8A"]
	fmt.Println(has8A)
}
No room set for 8C
true
  • The if with a short statement keeps room and ok inside the if. See Conditional Statements.
  • Use _ when you only need to know if the key exists.

Simple lesson: use v, ok := m[k] to tell a real zero value from a missing key.

Map Iteration

range

range over a map gives each key and value.

Loop over a map

package main

import "fmt"

func main() {
	fees := map[string]int{"Asha": 1500, "Ravi": 1200, "Meena": 1500}
	total := 0
	for _, fee := range fees {
		total += fee
	}
	fmt.Println("Total fees:", total)
}
Total fees: 4200

The Order Changes

Go walks through a map in a random order, and the order changes from run to run.

Run this twice: the order may change

package main

import "fmt"

func main() {
	houses := map[string]int{"Red": 120, "Blue": 95, "Green": 110, "Yellow": 101}
	for house, points := range houses {
		fmt.Println(house, points)
	}
}
  • Never depend on the order of a map loop.
  • If you need an order, put the keys in a slice and sort it first.

Print in sorted order

package main

import (
	"fmt"
	"sort"
)

func main() {
	houses := map[string]int{"Red": 120, "Blue": 95, "Green": 110}
	names := make([]string, 0, len(houses))
	for h := range houses {
		names = append(names, h)
	}
	sort.Strings(names)
	for _, h := range names {
		fmt.Println(h, houses[h])
	}
}
Blue 95
Green 110
Red 120
  • for h := range houses gives only the keys.
  • sort.Strings puts the keys in alphabetical order.

Simple lesson: range gives keys and values in a random order, so sort the keys when order matters.

Map Best Practices

Zero Values

The zero value makes counting easy: a missing key starts at 0.

Count votes for class monitor

package main

import "fmt"

func main() {
	votes := []string{"Asha", "Ravi", "Asha", "Meena", "Asha", "Ravi"}
	count := make(map[string]int)
	for _, name := range votes {
		count[name]++
	}
	fmt.Println(count)
}
map[Asha:3 Meena:1 Ravi:2]
  • count[name]++ works even the first time, because a missing key reads as 0.

Never Write to a nil Map

Writing to a nil map

package main

import "fmt"

func main() {
	var marks map[string]int
	fmt.Println(marks["Asha"])
	marks["Asha"] = 91
}
  • Reading from a nil map is fine: it gives 0.
  • Writing to it stops the program: panic: assignment to entry in nil map.
  • Fix: create it first with make or {}.

Nested Maps

Nested map = a map whose values are maps.

Marks by class, then by student

package main

import "fmt"

func main() {
	school := map[string]map[string]int{
		"8A": {"Asha": 91, "Meena": 85},
		"8B": {"Ravi": 78},
	}
	fmt.Println(school["8A"]["Meena"])

	if school["8C"] == nil {
		school["8C"] = make(map[string]int)
	}
	school["8C"]["Kabir"] = 94
	fmt.Println(len(school), school["8C"])
}
85
3 map[Kabir:94]
  • school["8A"]["Meena"] reads the inner map, then the student.
  • Before writing into a new inner map, create it. Otherwise it is nil.

Maps are not safe for two goroutines writing at the same time.

You will learn how to protect them in Synchronization.

Easy way to remember

  • Add or update: m[k] = v
  • Safe lookup: v, ok := m[k]
  • Remove: delete(m, k)
  • Order: none. Sort the keys if you need one.

Simple lesson: use the zero value for counting, always create a map before writing, and create inner maps too.

TL;DR

  1. Map = key-value pairs: map[string]int. Keys are unique.
  2. Create = map[K]V{...} or make(map[K]V). var m map[K]V is nil.
  3. Add / update = m[k] = v. Read = m[k]. Remove = delete(m, k).

    • Length = len(m).
  4. Missing key = the zero value, no error.
  5. Comma-ok = v, ok := m[k]. ok tells you if the key exists.
  6. range = for k, v := range m. The order is random.
  7. Best practices = count with m[k]++, never write to a nil map, create inner maps first.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
var m map[string]int then m["a"] = 1var makes an empty mapIt makes a nil mapassignment to entry in nil map
if m["Zoya"] == 0 to check a key0 means missing0 may be a real valueWrong answers. Use comma-ok.
Relying on loop orderMaps keep insertion orderThe order is randomOutput changes run to run.
map[[]int]stringAny type can be a keyKeys must be comparableA build error.
Forgetting to create an inner mapInner maps exist alreadyThey start as nilA panic on write.
delete(m, k) and checking an errorIt returns an errorIt returns nothingA build error.
Two goroutines writing one mapMaps are thread-safeThey are notconcurrent map writes crash.

Check Yourself

Exercises

What to Remember

  • A map stores values by key: map[string]int. Keys are unique.
  • Create with a literal or make. Never write to a nil map.
  • m[k] = v adds or updates. delete(m, k) removes.
  • A missing key gives the zero value. Use v, ok := m[k] to be sure.
  • Map loops have a random order. Sort the keys when you need one.

Next: Pointers. You will learn how a variable can point to another variable's box in memory.