Working With Maps For Key-Value Storage: Utilizing Maps For Dynamic Collections And Efficient Data Access
Overview
This chapter delves into Go’s powerful map data structure, a flexible key-value storage system essential for dynamic collections and efficient data retrieval. You’ll learn the fundamentals of map creation, initialization, and iteration, as well as techniques for updating, deleting, and checking the existence of values. With hands-on exercises, you’ll explore how to handle map errors, avoid common pitfalls, and manipulate complex data types stored in maps. By mastering Go maps, you will efficiently manage dynamic collections and improve your data access strategies.
Working With Maps For Key-Value Storage: Utilizing Maps For Dynamic Collections And Efficient Data Access
A map is an unordered set of values indexed by a unique key.
package main
import "fmt"
func main() {
beatles := map[string]string{}
beatles["John"] = "guitar"
beatles["Paul"] = "bass"
beatles["George"] = "guitar"
beatles["Ringo"] = "drums"
fmt.Println(beatles)
}package main
import "fmt"
func main() {
beatles := map[string]string{}
beatles["John"] = "guitar"
beatles["Paul"] = "bass"
beatles["George"] = "guitar"
beatles["Ringo"] = "drums"
fmt.Println(beatles)
}Initializing Maps
Maps can have their values assigned at creation time, just like arrays.
package main
import "fmt"
func main() {
beatles := map[string]string{
"John": "guitar",
"Paul": "bass",
"George": "guitar",
"Ringo": "drums",
}
fmt.Println(beatles)
}Uninitialized Maps
You can initialize maps two different ways:
emails := map[string]string{
"cory@gopherguides.com": "Cory LaNou",
"mark@gopherguides.com": "Mark Bates",
"tim@gopherguides.com": "Tim Raymond",
}package main
import "fmt"
func main() {
short()
initialize()
bad()
}
func short() {
// section: short
emails := map[string]string{
"cory@gopherguides.com": "Cory LaNou",
"mark@gopherguides.com": "Mark Bates",
"tim@gopherguides.com": "Tim Raymond",
}
// section: short
fmt.Println(emails)
}
func initialize() {
// section: make
var emails map[string]string
emails = make(map[string]string)
emails["cory@gopherguides.com"] = "Cory LaNou"
emails["mark@gopherguides.com"] = "Mark Bates"
emails["tim@gopherguides.com"] = "Tim Raymond"
// section: make
fmt.Println(emails)
}
func bad() {
// section: bad
var emails map[string]string
emails["cory@gopherguides.com"] = "Cory LaNou"
// section: bad
fmt.Println(emails)
}
var emails map[string]string
emails = make(map[string]string)
emails["cory@gopherguides.com"] = "Cory LaNou"
emails["mark@gopherguides.com"] = "Mark Bates"
emails["tim@gopherguides.com"] = "Tim Raymond"package main
import "fmt"
func main() {
short()
initialize()
bad()
}
func short() {
// section: short
emails := map[string]string{
"cory@gopherguides.com": "Cory LaNou",
"mark@gopherguides.com": "Mark Bates",
"tim@gopherguides.com": "Tim Raymond",
}
// section: short
fmt.Println(emails)
}
func initialize() {
// section: make
var emails map[string]string
emails = make(map[string]string)
emails["cory@gopherguides.com"] = "Cory LaNou"
emails["mark@gopherguides.com"] = "Mark Bates"
emails["tim@gopherguides.com"] = "Tim Raymond"
// section: make
fmt.Println(emails)
}
func bad() {
// section: bad
var emails map[string]string
emails["cory@gopherguides.com"] = "Cory LaNou"
// section: bad
fmt.Println(emails)
}If you don’t initialize them, and try to assign the values, you will receive a runtime error:
package main
import "fmt"
func main() {
short()
initialize()
bad()
}
func short() {
// section: short
emails := map[string]string{
"cory@gopherguides.com": "Cory LaNou",
"mark@gopherguides.com": "Mark Bates",
"tim@gopherguides.com": "Tim Raymond",
}
// section: short
fmt.Println(emails)
}
func initialize() {
// section: make
var emails map[string]string
emails = make(map[string]string)
emails["cory@gopherguides.com"] = "Cory LaNou"
emails["mark@gopherguides.com"] = "Mark Bates"
emails["tim@gopherguides.com"] = "Tim Raymond"
// section: make
fmt.Println(emails)
}
func bad() {
// section: bad
var emails map[string]string
emails["cory@gopherguides.com"] = "Cory LaNou"
// section: bad
fmt.Println(emails)
}panic: assignment to entry in nil map
Length And Capacity
The len function can be used to find the length (the number of keys) in the map.
fmt.Println(len(beatles))
Maps don’t have a capacity, since they can grow as needed, so the cap function would raise an error.
fmt.Println(cap(beatles))
error: invalid argument beatles (type map[string]string) for cap
Map Values
Map values can be set and retrieved using the the [] syntax.
package main
import "fmt"
func main() {
beatles := map[string]string{}
beatles["Paul"] = "bass"
paul := beatles["Paul"]
fmt.Println(paul) // bass
}Iterating Maps
Maps can be iterated over in the same ways as arrays and slices.
for key, value := range beatles {
fmt.Printf("%s plays %s\n", key, value)
}
// John plays guitar
// Paul plays bass
// George plays guitar
// Ringo plays drumspackage main
import "fmt"
func main() {
beatles := map[string]string{
"John": "guitar",
"Paul": "bass",
"George": "guitar",
"Ringo": "drums",
}
// section: code
for key, value := range beatles {
fmt.Printf("%s plays %s\n", key, value)
}
// John plays guitar
// Paul plays bass
// George plays guitar
// Ringo plays drums
// section: code
}The range keyword, in the case of maps, returns the key and the value for each entry in the map.
NOTE: When iterating over a map with a range loop, the iteration order is not specified and is not guaranteed to be the same from one iteration to the next.
Common Mistake: Relying On Map Iteration Order
Maps in Go are intentionally unordered. The iteration order is randomized on purpose to prevent developers from depending on it. This is one of the most common sources of bugs, especially in testing and output generation.
The Problem
Map iteration order is not deterministic. The same map can produce different ordering on each run:
func printConfig(cfg map[string]string) {
for key, value := range cfg {
fmt.Printf("%s: %s\n", key, value)
}
}
package main
import (
"fmt"
"slices"
)
var config = map[string]string{
"host": "localhost",
"port": "8080",
"username": "admin",
"password": "secret",
}
// section: unordered
func printConfig(cfg map[string]string) {
for key, value := range cfg {
fmt.Printf("%s: %s\n", key, value)
}
}
// section: unordered
// section: sorted
func printConfigSorted(cfg map[string]string) {
keys := make([]string, 0, len(cfg))
for key := range cfg {
keys = append(keys, key)
}
slices.Sort(keys)
for _, key := range keys {
fmt.Printf("%s: %s\n", key, cfg[key])
}
}
// section: sorted
// section: main
func main() {
fmt.Println("Unordered run 1:")
printConfig(config)
fmt.Println("\nUnordered run 2:")
printConfig(config)
fmt.Println("\nSorted iteration:")
printConfigSorted(config)
}
// section: main
/*
// section: output
# Output captured with GODEBUG=randmapiter=1 (order will vary each run)
Unordered run 1:
host: localhost
port: 8080
username: admin
password: secret
Unordered run 2:
password: secret
host: localhost
port: 8080
username: admin
Sorted iteration:
host: localhost
password: secret
port: 8080
username: admin
// section: output
*/Output from two iterations in the same run (using Go’s randmapiter debug flag to emphasize the nondeterminism):
# Output captured with GODEBUG=randmapiter=1 (order will vary each run)
Unordered run 1:
host: localhost
port: 8080
username: admin
password: secret
Unordered run 2:
password: secret
host: localhost
port: 8080
username: admin
Sorted iteration:
host: localhost
password: secret
port: 8080
username: adminpackage main
import (
"fmt"
"slices"
)
var config = map[string]string{
"host": "localhost",
"port": "8080",
"username": "admin",
"password": "secret",
}
// section: unordered
func printConfig(cfg map[string]string) {
for key, value := range cfg {
fmt.Printf("%s: %s\n", key, value)
}
}
// section: unordered
// section: sorted
func printConfigSorted(cfg map[string]string) {
keys := make([]string, 0, len(cfg))
for key := range cfg {
keys = append(keys, key)
}
slices.Sort(keys)
for _, key := range keys {
fmt.Printf("%s: %s\n", key, cfg[key])
}
}
// section: sorted
// section: main
func main() {
fmt.Println("Unordered run 1:")
printConfig(config)
fmt.Println("\nUnordered run 2:")
printConfig(config)
fmt.Println("\nSorted iteration:")
printConfigSorted(config)
}
// section: main
/*
// section: output
# Output captured with GODEBUG=randmapiter=1 (order will vary each run)
Unordered run 1:
host: localhost
port: 8080
username: admin
password: secret
Unordered run 2:
password: secret
host: localhost
port: 8080
username: admin
Sorted iteration:
host: localhost
password: secret
port: 8080
username: admin
// section: output
*/Real-World Scenarios Where This Causes Bugs
1. Flaky Tests
Tests that compare output strings will fail randomly:
func TestConfig(t *testing.T) {
config := map[string]string{"a": "1", "b": "2", "c": "3"}
var output string
for k, v := range config {
output += fmt.Sprintf("%s=%s ", k, v)
}
// This test is FLAKY - will fail randomly!
expected := "a=1 b=2 c=3 "
if output != expected {
t.Errorf("got %q, want %q", output, expected)
}
}
2. Inconsistent Output Generation
Configuration files or reports generated from maps will have different ordering:
// BAD: config.ini will be different every time
func WriteConfig(config map[string]string) {
for key, value := range config {
fmt.Fprintf(file, "%s=%s\n", key, value)
}
}
3. Serialization Issues
JSON/XML output from maps may differ between runs, breaking checksums or comparisons:
// Output order is unpredictable
for key := range data {
fmt.Fprintf(w, "<item>%s</item>", key)
}
The Solution: Sort Map Keys First
When order matters, extract and sort the keys before iteration:
func printConfigSorted(cfg map[string]string) {
keys := make([]string, 0, len(cfg))
for key := range cfg {
keys = append(keys, key)
}
slices.Sort(keys)
for _, key := range keys {
fmt.Printf("%s: %s\n", key, cfg[key])
}
}
package main
import (
"fmt"
"slices"
)
var config = map[string]string{
"host": "localhost",
"port": "8080",
"username": "admin",
"password": "secret",
}
// section: unordered
func printConfig(cfg map[string]string) {
for key, value := range cfg {
fmt.Printf("%s: %s\n", key, value)
}
}
// section: unordered
// section: sorted
func printConfigSorted(cfg map[string]string) {
keys := make([]string, 0, len(cfg))
for key := range cfg {
keys = append(keys, key)
}
slices.Sort(keys)
for _, key := range keys {
fmt.Printf("%s: %s\n", key, cfg[key])
}
}
// section: sorted
// section: main
func main() {
fmt.Println("Unordered run 1:")
printConfig(config)
fmt.Println("\nUnordered run 2:")
printConfig(config)
fmt.Println("\nSorted iteration:")
printConfigSorted(config)
}
// section: main
/*
// section: output
# Output captured with GODEBUG=randmapiter=1 (order will vary each run)
Unordered run 1:
host: localhost
port: 8080
username: admin
password: secret
Unordered run 2:
password: secret
host: localhost
port: 8080
username: admin
Sorted iteration:
host: localhost
password: secret
port: 8080
username: admin
// section: output
*/Every run produces the same output:
# Output captured with GODEBUG=randmapiter=1 (order will vary each run)
Unordered run 1:
host: localhost
port: 8080
username: admin
password: secret
Unordered run 2:
password: secret
host: localhost
port: 8080
username: admin
Sorted iteration:
host: localhost
password: secret
port: 8080
username: adminpackage main
import (
"fmt"
"slices"
)
var config = map[string]string{
"host": "localhost",
"port": "8080",
"username": "admin",
"password": "secret",
}
// section: unordered
func printConfig(cfg map[string]string) {
for key, value := range cfg {
fmt.Printf("%s: %s\n", key, value)
}
}
// section: unordered
// section: sorted
func printConfigSorted(cfg map[string]string) {
keys := make([]string, 0, len(cfg))
for key := range cfg {
keys = append(keys, key)
}
slices.Sort(keys)
for _, key := range keys {
fmt.Printf("%s: %s\n", key, cfg[key])
}
}
// section: sorted
// section: main
func main() {
fmt.Println("Unordered run 1:")
printConfig(config)
fmt.Println("\nUnordered run 2:")
printConfig(config)
fmt.Println("\nSorted iteration:")
printConfigSorted(config)
}
// section: main
/*
// section: output
# Output captured with GODEBUG=randmapiter=1 (order will vary each run)
Unordered run 1:
host: localhost
port: 8080
username: admin
password: secret
Unordered run 2:
password: secret
host: localhost
port: 8080
username: admin
Sorted iteration:
host: localhost
password: secret
port: 8080
username: admin
// section: output
*/When Order Matters Vs. When It Doesn't
Order matters when:
- Generating output for users (reports, config files, logs)
- Writing tests that compare output strings
- Creating deterministic output (checksums, signatures)
- Displaying sorted lists in UIs
- Generating documentation
Order doesn’t matter when:
- Looking up values by key
- Checking membership (
_, ok := map[key]) - Accumulating results (counts, sums)
- Building another map or set
- Performing operations where order is irrelevant
Complete Example: Fixing A Flaky Test
Before (Flaky):
func TestGenerateConfig(t *testing.T) {
settings := map[string]string{
"timeout": "30",
"retries": "3",
"debug": "true",
}
output := GenerateConfig(settings)
// FLAKY: Order is random
expected := "timeout=30\nretries=3\ndebug=true\n"
if output != expected {
t.Errorf("got %q, want %q", output, expected)
}
}
After (Reliable):
func TestGenerateConfig(t *testing.T) {
settings := map[string]string{
"timeout": "30",
"retries": "3",
"debug": "true",
}
output := GenerateConfig(settings)
// RELIABLE: Expected output is sorted
expected := "debug=true\nretries=3\ntimeout=30\n"
if output != expected {
t.Errorf("got %q, want %q", output, expected)
}
}
func GenerateConfig(settings map[string]string) string {
// Sort keys for deterministic output
keys := make([]string, 0, len(settings))
for k := range settings {
keys = append(keys, k)
}
slices.Sort(keys)
var result string
for _, key := range keys {
result += fmt.Sprintf("%s=%s\n", key, settings[key])
}
return result
}
Remember: Never rely on map iteration order. Always sort keys when order matters.
Map Keys
Map keys must be comparable.
You can’t use functions, maps, or slices as the keys in your maps.
package main
import "fmt"
func main() {
// section: key
m := map[func()]string{}
fmt.Println(m)
// section: key
}Results in the following error:
./map-keys.go:6: invalid map key type func()
Some structs are ok, but again, can’t contain complex types:
type simple struct {
ID int
}
type complex struct {
f func(id int) simple
}
func main() {
m := map[simple]string{}
fmt.Println(m)
// invalid map key type complex
m1 := map[complex]string{}
}
Deleting Keys From A Map
The delete function can be used to remove a key, and its value, from a map.
package main
import "fmt"
func main() {
beatles := map[string]string{
"John": "guitar",
"Paul": "bass",
"George": "guitar",
"Ringo": "drums",
}
delete(beatles, "John")
fmt.Println(beatles)
// map[Paul:bass George:guitar Ringo:drums]
}Checking Map Values
Maps in Go return an optional second argument that will tell you if the key exists in the map.
key := "Paul"
value, ok := beatles[key]
if ok {
fmt.Printf("Found key %q: %q", key, value) // Found Key "Paul": "bass"
} else {
fmt.Printf("Key not found: %q", key) // Key not found: "foo"
}
// Change the "key" to "foo" and re-run the examplepackage main
import "fmt"
func main() {
beatles := map[string]string{
"John": "guitar",
"Paul": "bass",
"George": "guitar",
"Ringo": "drums",
}
// section: code
key := "Paul"
value, ok := beatles[key]
if ok {
fmt.Printf("Found key %q: %q", key, value) // Found Key "Paul": "bass"
} else {
fmt.Printf("Key not found: %q", key) // Key not found: "foo"
}
// Change the "key" to "foo" and re-run the example
// section: code
}Which can, and will often be, simplified as the following:
key := "Paul"
if value, ok := beatles[key]; ok {
fmt.Printf("Found key %q: %q", key, value) // Found Key "Paul": "bass"
} else {
fmt.Printf("Key not found: %q", key) // Key not found: "foo"
}
// Change the "key" to "foo" and re-run the examplepackage main
import "fmt"
func main() {
beatles := map[string]string{
"John": "guitar",
"Paul": "bass",
"George": "guitar",
"Ringo": "drums",
}
// section: code
key := "Paul"
if value, ok := beatles[key]; ok {
fmt.Printf("Found key %q: %q", key, value) // Found Key "Paul": "bass"
} else {
fmt.Printf("Key not found: %q", key) // Key not found: "foo"
}
// Change the "key" to "foo" and re-run the example
// section: code
}Bug Time
In the following example, we show that even if we don’t get an error, you still have a bug because we didn’t check for the existence of the value
type simple struct {
ID int
Name string
}
func main() {
data := map[int]simple{}
s1 := simple{ID: 1, Name: "Cory"}
data[1] = s1
value := data[10]
// Because of the way Zero values in Go work,
// we still get an `zero` value representation of the struct
// which is certainly a bug in production
fmt.Printf("%+v", value)
}
package main
import "fmt"
// section: code
type simple struct {
ID int
Name string
}
func main() {
data := map[int]simple{}
s1 := simple{ID: 1, Name: "Cory"}
data[1] = s1
value := data[10]
// Because of the way Zero values in Go work,
// we still get an `zero` value representation of the struct
// which is certainly a bug in production
fmt.Printf("%+v", value)
}
// section: code
/*
// section: expected
{ID:1 Name:Cory}
// section: expected
// section: output
{ID:0 Name:}
// section: output
*/Desired output:
package main
import "fmt"
// section: code
type simple struct {
ID int
Name string
}
func main() {
data := map[int]simple{}
s1 := simple{ID: 1, Name: "Cory"}
data[1] = s1
value := data[10]
// Because of the way Zero values in Go work,
// we still get an `zero` value representation of the struct
// which is certainly a bug in production
fmt.Printf("%+v", value)
}
// section: code
/*
// section: expected
{ID:1 Name:Cory}
// section: expected
// section: output
{ID:0 Name:}
// section: output
*/Actual Output:
package main
import "fmt"
// section: code
type simple struct {
ID int
Name string
}
func main() {
data := map[int]simple{}
s1 := simple{ID: 1, Name: "Cory"}
data[1] = s1
value := data[10]
// Because of the way Zero values in Go work,
// we still get an `zero` value representation of the struct
// which is certainly a bug in production
fmt.Printf("%+v", value)
}
// section: code
/*
// section: expected
{ID:1 Name:Cory}
// section: expected
// section: output
{ID:0 Name:}
// section: output
*/Bug Solution
Be sure to check for the existence of the key in your code to avoid zero value being being returned and creating a bug:
type simple struct {
ID int
Name string
}
func main() {
data := map[int]simple{}
s1 := simple{ID: 1, Name: "Cory"}
data[1] = s1
if value, ok := data[10]; ok {
fmt.Printf("%+v", value)
return
}
fmt.Println("value not found")
}
Exploiting Zero Value
There may be times when the zero value is ok.
counts := make(map[string]int)
sentence := "The quick brown fox jumps over the lazy dog"
words := strings.Fields(strings.ToLower(sentence))
for _, w := range words {
counts[w]++
}package main
import (
"fmt"
"strings"
)
func main() {
zero()
}
func zero() {
// section: code
counts := make(map[string]int)
sentence := "The quick brown fox jumps over the lazy dog"
words := strings.Fields(strings.ToLower(sentence))
for _, w := range words {
counts[w]++
}
// section: code
fmt.Println(counts)
}
/*
// section: output
map[brown:1 dog:1 fox:1 jumps:1 lazy:1 over:1 quick:1 the:2]
// section: output
*/Because the zero value for any lookup is 0, we can safely use the increment operator and it will then set the initial key/value.
The value of the map will be:
package main
import (
"fmt"
"strings"
)
func main() {
zero()
}
func zero() {
// section: code
counts := make(map[string]int)
sentence := "The quick brown fox jumps over the lazy dog"
words := strings.Fields(strings.ToLower(sentence))
for _, w := range words {
counts[w]++
}
// section: code
fmt.Println(counts)
}
/*
// section: output
map[brown:1 dog:1 fox:1 jumps:1 lazy:1 over:1 quick:1 the:2]
// section: output
*/Testing Presence Only
There may be times when you don’t want the value and only want to test for presence. You can use the _ (underscore) operator to ignore the value in these situations:
words := map[string]int{
"brown": 1,
"dog": 1,
"fox": 1,
"jumps": 1,
"lazy": 1,
"over": 1,
"quick": 1,
"the": 2,
}
_, exists := words["foo"]
fmt.Println(exists)
package main
import "fmt"
func main() {
presence()
}
func presence() {
// section: code
words := map[string]int{
"brown": 1,
"dog": 1,
"fox": 1,
"jumps": 1,
"lazy": 1,
"over": 1,
"quick": 1,
"the": 2,
}
_, exists := words["foo"]
fmt.Println(exists)
// section: code
}
/*
// section: output
false
// section: output
*/Output:
package main
import "fmt"
func main() {
presence()
}
func presence() {
// section: code
words := map[string]int{
"brown": 1,
"dog": 1,
"fox": 1,
"jumps": 1,
"lazy": 1,
"over": 1,
"quick": 1,
"the": 2,
}
_, exists := words["foo"]
fmt.Println(exists)
// section: code
}
/*
// section: output
false
// section: output
*/Maps And Complex Values
Storing complex values (such as structs) in a map is a very common operation. However, updating those structs is not completely straightforward.
It may seem intutive to simply assign a new value to a struct via a map lookup:
type simple struct {
ID int
Name string
}
func main() {
data := map[int]simple{}
s1 := simple{ID: 1, Name: "Cory"}
data[1] = s1
data[1].Name = "Jill" // <== Update the struct
fmt.Printf("%+v", data)
}
package main
import "fmt"
// section: code
type simple struct {
ID int
Name string
}
func main() {
data := map[int]simple{}
s1 := simple{ID: 1, Name: "Cory"}
data[1] = s1
data[1].Name = "Jill" // <== Update the struct
fmt.Printf("%+v", data)
}
// section: code
/*
// section: output
./main.go:16:15: cannot assign to struct field data[1].Name in map
// section: output
*/Output:
package main
import "fmt"
// section: code
type simple struct {
ID int
Name string
}
func main() {
data := map[int]simple{}
s1 := simple{ID: 1, Name: "Cory"}
data[1] = s1
data[1].Name = "Jill" // <== Update the struct
fmt.Printf("%+v", data)
}
// section: code
/*
// section: output
./main.go:16:15: cannot assign to struct field data[1].Name in map
// section: output
*/Updating Complex Map Values
When updating complex map values, you have to retrieve the value, make your changes, and set it back into the map:
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
// Create Beatle dynamically
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
// Create Beatle from an instance
b := Beatle{Name: "Paul", Instrument: "bass"}
beatles[b.Name] = b
b = Beatle{Name: "George"}
beatles[b.Name] = b
b = Beatle{Name: "Ringo", Instrument: "Drums"}
beatles[b.Name] = b
// Fix George:
// Lookup George
b, ok := beatles["George"]
if !ok {
fmt.Println("couldn't find George...")
return
}
// Update George
b.Instrument = "guitar"
// Update the map with the new value
beatles[b.Name] = b
fmt.Println(beatles)package main
import "fmt"
func main() {
// section: code
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
// Create Beatle dynamically
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
// Create Beatle from an instance
b := Beatle{Name: "Paul", Instrument: "bass"}
beatles[b.Name] = b
b = Beatle{Name: "George"}
beatles[b.Name] = b
b = Beatle{Name: "Ringo", Instrument: "Drums"}
beatles[b.Name] = b
// Fix George:
// Lookup George
b, ok := beatles["George"]
if !ok {
fmt.Println("couldn't find George...")
return
}
// Update George
b.Instrument = "guitar"
// Update the map with the new value
beatles[b.Name] = b
fmt.Println(beatles)
// section: code
}
/*
// section: output
map[George:{George guitar} John:{John guitar} Paul:{Paul bass} Ringo:{Ringo Drums}]
// section: output
*/Output:
package main
import "fmt"
func main() {
// section: code
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
// Create Beatle dynamically
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
// Create Beatle from an instance
b := Beatle{Name: "Paul", Instrument: "bass"}
beatles[b.Name] = b
b = Beatle{Name: "George"}
beatles[b.Name] = b
b = Beatle{Name: "Ringo", Instrument: "Drums"}
beatles[b.Name] = b
// Fix George:
// Lookup George
b, ok := beatles["George"]
if !ok {
fmt.Println("couldn't find George...")
return
}
// Update George
b.Instrument = "guitar"
// Update the map with the new value
beatles[b.Name] = b
fmt.Println(beatles)
// section: code
}
/*
// section: output
map[George:{George guitar} John:{John guitar} Paul:{Paul bass} Ringo:{Ringo Drums}]
// section: output
*/Range Bug
It’s important to remember that when you use the range operator, that the loop variables are a copy of the value, and not a reference to the value:
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
beatles["Paul"] = Beatle{Name: "Paul", Instrument: "bass"}
beatles["George"] = Beatle{Name: "George", Instrument: "guitar"}
beatles["Ringo"] = Beatle{Name: "Ringo", Instrument: "drums"}
for _, b := range beatles {
// `b` is a COPY, and will not update the value stored in the map
b.Instrument = strings.ToUpper(b.Instrument)
fmt.Println(b)
}
fmt.Println(beatles)package main
import (
"fmt"
"strings"
)
func main() {
cpy()
key()
}
func cpy() {
// section: copy
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
beatles["Paul"] = Beatle{Name: "Paul", Instrument: "bass"}
beatles["George"] = Beatle{Name: "George", Instrument: "guitar"}
beatles["Ringo"] = Beatle{Name: "Ringo", Instrument: "drums"}
for _, b := range beatles {
// `b` is a COPY, and will not update the value stored in the map
b.Instrument = strings.ToUpper(b.Instrument)
fmt.Println(b)
}
fmt.Println(beatles)
// section: copy
/*
// section: copy-output
{John GUITAR}
{Paul BASS}
{George GUITAR}
{Ringo DRUMS}
map[George:{George guitar} John:{John guitar} Paul:{Paul bass} Ringo:{Ringo drums}]
// section: copy-output
*/
}
func key() {
// section: key
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
beatles["Paul"] = Beatle{Name: "Paul", Instrument: "bass"}
beatles["George"] = Beatle{Name: "George", Instrument: "guitar"}
beatles["Ringo"] = Beatle{Name: "Ringo", Instrument: "drums"}
for k, b := range beatles {
b.Instrument = strings.ToUpper(b.Instrument)
fmt.Println(b)
// update the map:
beatles[k] = b
}
fmt.Println(beatles)
// section: key
/*
// section: key-output
{John GUITAR}
{Paul BASS}
{George GUITAR}
{Ringo DRUMS}
map[George:{George GUITAR} John:{John GUITAR} Paul:{Paul BASS} Ringo:{Ringo DRUMS}]
// section: key-output
*/
}Ouptut:
{John GUITAR}
{Paul BASS}
{George GUITAR}
{Ringo DRUMS}
map[George:{George guitar} John:{John guitar} Paul:{Paul bass} Ringo:{Ringo drums}]package main
import (
"fmt"
"strings"
)
func main() {
cpy()
key()
}
func cpy() {
// section: copy
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
beatles["Paul"] = Beatle{Name: "Paul", Instrument: "bass"}
beatles["George"] = Beatle{Name: "George", Instrument: "guitar"}
beatles["Ringo"] = Beatle{Name: "Ringo", Instrument: "drums"}
for _, b := range beatles {
// `b` is a COPY, and will not update the value stored in the map
b.Instrument = strings.ToUpper(b.Instrument)
fmt.Println(b)
}
fmt.Println(beatles)
// section: copy
/*
// section: copy-output
{John GUITAR}
{Paul BASS}
{George GUITAR}
{Ringo DRUMS}
map[George:{George guitar} John:{John guitar} Paul:{Paul bass} Ringo:{Ringo drums}]
// section: copy-output
*/
}
func key() {
// section: key
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
beatles["Paul"] = Beatle{Name: "Paul", Instrument: "bass"}
beatles["George"] = Beatle{Name: "George", Instrument: "guitar"}
beatles["Ringo"] = Beatle{Name: "Ringo", Instrument: "drums"}
for k, b := range beatles {
b.Instrument = strings.ToUpper(b.Instrument)
fmt.Println(b)
// update the map:
beatles[k] = b
}
fmt.Println(beatles)
// section: key
/*
// section: key-output
{John GUITAR}
{Paul BASS}
{George GUITAR}
{Ringo DRUMS}
map[George:{George GUITAR} John:{John GUITAR} Paul:{Paul BASS} Ringo:{Ringo DRUMS}]
// section: key-output
*/
}Range Solution
To ensure you are updating the correct value, always use the key when updating a map:
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
beatles["Paul"] = Beatle{Name: "Paul", Instrument: "bass"}
beatles["George"] = Beatle{Name: "George", Instrument: "guitar"}
beatles["Ringo"] = Beatle{Name: "Ringo", Instrument: "drums"}
for k, b := range beatles {
b.Instrument = strings.ToUpper(b.Instrument)
fmt.Println(b)
// update the map:
beatles[k] = b
}
fmt.Println(beatles)package main
import (
"fmt"
"strings"
)
func main() {
cpy()
key()
}
func cpy() {
// section: copy
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
beatles["Paul"] = Beatle{Name: "Paul", Instrument: "bass"}
beatles["George"] = Beatle{Name: "George", Instrument: "guitar"}
beatles["Ringo"] = Beatle{Name: "Ringo", Instrument: "drums"}
for _, b := range beatles {
// `b` is a COPY, and will not update the value stored in the map
b.Instrument = strings.ToUpper(b.Instrument)
fmt.Println(b)
}
fmt.Println(beatles)
// section: copy
/*
// section: copy-output
{John GUITAR}
{Paul BASS}
{George GUITAR}
{Ringo DRUMS}
map[George:{George guitar} John:{John guitar} Paul:{Paul bass} Ringo:{Ringo drums}]
// section: copy-output
*/
}
func key() {
// section: key
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
beatles["Paul"] = Beatle{Name: "Paul", Instrument: "bass"}
beatles["George"] = Beatle{Name: "George", Instrument: "guitar"}
beatles["Ringo"] = Beatle{Name: "Ringo", Instrument: "drums"}
for k, b := range beatles {
b.Instrument = strings.ToUpper(b.Instrument)
fmt.Println(b)
// update the map:
beatles[k] = b
}
fmt.Println(beatles)
// section: key
/*
// section: key-output
{John GUITAR}
{Paul BASS}
{George GUITAR}
{Ringo DRUMS}
map[George:{George GUITAR} John:{John GUITAR} Paul:{Paul BASS} Ringo:{Ringo DRUMS}]
// section: key-output
*/
}Ouptut:
{John GUITAR}
{Paul BASS}
{George GUITAR}
{Ringo DRUMS}
map[George:{George GUITAR} John:{John GUITAR} Paul:{Paul BASS} Ringo:{Ringo DRUMS}]package main
import (
"fmt"
"strings"
)
func main() {
cpy()
key()
}
func cpy() {
// section: copy
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
beatles["Paul"] = Beatle{Name: "Paul", Instrument: "bass"}
beatles["George"] = Beatle{Name: "George", Instrument: "guitar"}
beatles["Ringo"] = Beatle{Name: "Ringo", Instrument: "drums"}
for _, b := range beatles {
// `b` is a COPY, and will not update the value stored in the map
b.Instrument = strings.ToUpper(b.Instrument)
fmt.Println(b)
}
fmt.Println(beatles)
// section: copy
/*
// section: copy-output
{John GUITAR}
{Paul BASS}
{George GUITAR}
{Ringo DRUMS}
map[George:{George guitar} John:{John guitar} Paul:{Paul bass} Ringo:{Ringo drums}]
// section: copy-output
*/
}
func key() {
// section: key
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
beatles["John"] = Beatle{Name: "John", Instrument: "guitar"}
beatles["Paul"] = Beatle{Name: "Paul", Instrument: "bass"}
beatles["George"] = Beatle{Name: "George", Instrument: "guitar"}
beatles["Ringo"] = Beatle{Name: "Ringo", Instrument: "drums"}
for k, b := range beatles {
b.Instrument = strings.ToUpper(b.Instrument)
fmt.Println(b)
// update the map:
beatles[k] = b
}
fmt.Println(beatles)
// section: key
/*
// section: key-output
{John GUITAR}
{Paul BASS}
{George GUITAR}
{Ringo DRUMS}
map[George:{George GUITAR} John:{John GUITAR} Paul:{Paul BASS} Ringo:{Ringo DRUMS}]
// section: key-output
*/
}Copy On Update
When you insert into a map, it takes a copy of what you are inserting, and you no longer have a reference to the value stored in the map.
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
b := Beatle{Name: "Paul", Instrument: "bass"}
beatles[b.Name] = b
// change the value after being inserted into the map
b.Instrument = "foo"
fmt.Println("variable:", b)
// retrieve the value from the map
lookup := beatles["Paul"]
fmt.Println("lookup:", lookup)package main
import "fmt"
func main() {
// section: code
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
b := Beatle{Name: "Paul", Instrument: "bass"}
beatles[b.Name] = b
// change the value after being inserted into the map
b.Instrument = "foo"
fmt.Println("variable:", b)
// retrieve the value from the map
lookup := beatles["Paul"]
fmt.Println("lookup:", lookup)
// section: code
}
/*
// section: output
variable: {Paul foo}
lookup: {Paul bass}
// section: output
*/Ouptut:
package main
import "fmt"
func main() {
// section: code
type Beatle struct {
Name string
Instrument string
}
beatles := make(map[string]Beatle)
b := Beatle{Name: "Paul", Instrument: "bass"}
beatles[b.Name] = b
// change the value after being inserted into the map
b.Instrument = "foo"
fmt.Println("variable:", b)
// retrieve the value from the map
lookup := beatles["Paul"]
fmt.Println("lookup:", lookup)
// section: code
}
/*
// section: output
variable: {Paul foo}
lookup: {Paul bass}
// section: output
*/Getting Map Keys
Go does not provide a way to get just a list of keys, or values, from a map. To do this you must build that list yourself.
package main
import "fmt"
func main() {
m := map[string]string{
"foo": "bar",
"bin": "baz",
"boo": "woo",
}
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
fmt.Printf("%+v", keys)
}The Maps Package
The maps package, added to the standard library in Go 1.23, provides iterator-based helpers for working with maps (Keys, Values, Copy, DeleteFunc, Equal/EqualFunc, All, and more).
Go 1.21 introduced the built-in clear for maps and slices; Go 1.23 added the maps package; Go 1.24 improved large-map performance and shipped weak for memory-efficient structures
Maps.Clone
The maps.Clone function creates a shallow copy of a map. Changes to the cloned map don’t affect the original:
original := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
// Clone creates a shallow copy of the map
cloned := maps.Clone(original)
// Modify the cloned map
cloned["Alice"] = 99
cloned["David"] = 40
fmt.Println("Original map:", original)
fmt.Println("Cloned map: ", cloned)package main
import (
"fmt"
"maps"
)
func main() {
// section: example
original := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
// Clone creates a shallow copy of the map
cloned := maps.Clone(original)
// Modify the cloned map
cloned["Alice"] = 99
cloned["David"] = 40
fmt.Println("Original map:", original)
fmt.Println("Cloned map: ", cloned)
// section: example
}Maps.Copy
The maps.Copy function copies all key-value pairs from a source map to a destination map. Existing keys in the destination are overwritten:
src := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
dst := map[string]int{
"Bob": 99, // This will be overwritten
"Eve": 28, // This will remain
}
fmt.Println("Before copy:")
fmt.Println(" Source: ", src)
fmt.Println(" Destination:", dst)
// Copy all key-value pairs from src to dst
maps.Copy(dst, src)
fmt.Println("\nAfter copy:")
fmt.Println(" Source: ", src)
fmt.Println(" Destination:", dst)package main
import (
"fmt"
"maps"
)
func main() {
// section: example
src := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
dst := map[string]int{
"Bob": 99, // This will be overwritten
"Eve": 28, // This will remain
}
fmt.Println("Before copy:")
fmt.Println(" Source: ", src)
fmt.Println(" Destination:", dst)
// Copy all key-value pairs from src to dst
maps.Copy(dst, src)
fmt.Println("\nAfter copy:")
fmt.Println(" Source: ", src)
fmt.Println(" Destination:", dst)
// section: example
}Maps.Equal
The maps.Equal function compares two maps for equality. Two maps are equal if they have the same keys and values:
map1 := map[string]int{
"Alice": 25,
"Bob": 30,
}
map2 := map[string]int{
"Bob": 30,
"Alice": 25,
}
map3 := map[string]int{
"Alice": 25,
"Bob": 31, // Different value
}
fmt.Println("map1 == map2:", maps.Equal(map1, map2))
fmt.Println("map1 == map3:", maps.Equal(map1, map3))package main
import (
"fmt"
"maps"
)
func main() {
// section: example
map1 := map[string]int{
"Alice": 25,
"Bob": 30,
}
map2 := map[string]int{
"Bob": 30,
"Alice": 25,
}
map3 := map[string]int{
"Alice": 25,
"Bob": 31, // Different value
}
fmt.Println("map1 == map2:", maps.Equal(map1, map2))
fmt.Println("map1 == map3:", maps.Equal(map1, map3))
// section: example
}Maps.DeleteFunc
The maps.DeleteFunc function deletes all key-value pairs that satisfy a predicate function:
scores := map[string]int{
"Alice": 85,
"Bob": 92,
"Charlie": 78,
"David": 45,
"Eve": 67,
}
fmt.Println("Before DeleteFunc:", scores)
// Delete all entries where the score is below 70
maps.DeleteFunc(scores, func(name string, score int) bool {
return score < 70
})
fmt.Println("After DeleteFunc: ", scores)package main
import (
"fmt"
"maps"
)
func main() {
// section: example
scores := map[string]int{
"Alice": 85,
"Bob": 92,
"Charlie": 78,
"David": 45,
"Eve": 67,
}
fmt.Println("Before DeleteFunc:", scores)
// Delete all entries where the score is below 70
maps.DeleteFunc(scores, func(name string, score int) bool {
return score < 70
})
fmt.Println("After DeleteFunc: ", scores)
// section: example
}Maps.Keys
The maps.Keys function returns an iterator over the keys of a map, so you can build a slice (or range directly) without writing a manual loop:
m := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
// Get an iterator over the keys, then collect and sort for stable output
keys := maps.Keys(m)
keySlice := slices.Collect(keys)
slices.Sort(keySlice)
fmt.Println("Keys:", keySlice)package main
import (
"fmt"
"maps"
"slices"
)
func main() {
// section: example
m := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
// Get an iterator over the keys, then collect and sort for stable output
keys := maps.Keys(m)
keySlice := slices.Collect(keys)
slices.Sort(keySlice)
fmt.Println("Keys:", keySlice)
// section: example
}Maps.Values
The maps.Values function returns an iterator over the values of a map:
m := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
// Get an iterator over the values, then collect and sort for stable output
values := maps.Values(m)
valueSlice := slices.Collect(values)
slices.Sort(valueSlice)
fmt.Println("Values:", valueSlice)package main
import (
"fmt"
"maps"
"slices"
)
func main() {
// section: example
m := map[string]int{
"Alice": 25,
"Bob": 30,
"Charlie": 35,
}
// Get an iterator over the values, then collect and sort for stable output
values := maps.Values(m)
valueSlice := slices.Collect(values)
slices.Sort(valueSlice)
fmt.Println("Values:", valueSlice)
// section: example
}Sorting Maps
As you recall, we covered earlier that maps are not sorted. Given the following code:
func main() {
months := map[int]string{
1: "January",
2: "February",
3: "March",
4: "April",
5: "May",
6: "June",
7: "July",
8: "August",
9: "September",
10: "October",
11: "November",
12: "December",
}
for k, v := range months {
fmt.Println(k, v)
}
}
package main
import (
"fmt"
)
// section: code
func main() {
months := map[int]string{
1: "January",
2: "February",
3: "March",
4: "April",
5: "May",
6: "June",
7: "July",
8: "August",
9: "September",
10: "October",
11: "November",
12: "December",
}
for k, v := range months {
fmt.Println(k, v)
}
}
// section: code
/*
// section: output
$ go run ./keys-unsorted.go
6 June
8 August
12 December
9 September
10 October
1 January
2 February
3 March
4 April
5 May
7 July
11 November
// section: output
*/You will receive the following output:
$ go run ./keys-unsorted.go
6 June
8 August
12 December
9 September
10 October
1 January
2 February
3 March
4 April
5 May
7 July
11 Novemberpackage main
import (
"fmt"
)
// section: code
func main() {
months := map[int]string{
1: "January",
2: "February",
3: "March",
4: "April",
5: "May",
6: "June",
7: "July",
8: "August",
9: "September",
10: "October",
11: "November",
12: "December",
}
for k, v := range months {
fmt.Println(k, v)
}
}
// section: code
/*
// section: output
$ go run ./keys-unsorted.go
6 June
8 August
12 December
9 September
10 October
1 January
2 February
3 March
4 April
5 May
7 July
11 November
// section: output
*/Sorting By Key
If you want to sort the map by the key, gather the keys, and use the sort package to sort the keys:
func main() {
months := map[int]string{
1: "January",
2: "February",
3: "March",
4: "April",
5: "May",
6: "June",
7: "July",
8: "August",
9: "September",
10: "October",
11: "November",
12: "December",
}
keys := make([]int, 0, len(months))
for k := range months {
keys = append(keys, k)
}
sort.Ints(keys)
fmt.Printf("keys: %+v\n", keys)
for _, k := range keys {
fmt.Println(k, months[k])
}
}
package main
import (
"fmt"
"sort"
)
// section: code
func main() {
months := map[int]string{
1: "January",
2: "February",
3: "March",
4: "April",
5: "May",
6: "June",
7: "July",
8: "August",
9: "September",
10: "October",
11: "November",
12: "December",
}
keys := make([]int, 0, len(months))
for k := range months {
keys = append(keys, k)
}
sort.Ints(keys)
fmt.Printf("keys: %+v\n", keys)
for _, k := range keys {
fmt.Println(k, months[k])
}
}
// section: code
/*
// section: output
keys: [1 2 3 4 5 6 7 8 9 10 11 12]
1 January
2 February
3 March
4 April
5 May
6 June
7 July
8 August
9 September
10 October
11 November
12 December
// section: output
*/And now you can use the sorted slice you created to iterate back through the map in the order you created:
keys: [1 2 3 4 5 6 7 8 9 10 11 12]
1 January
2 February
3 March
4 April
5 May
6 June
7 July
8 August
9 September
10 October
11 November
12 Decemberpackage main
import (
"fmt"
"sort"
)
// section: code
func main() {
months := map[int]string{
1: "January",
2: "February",
3: "March",
4: "April",
5: "May",
6: "June",
7: "July",
8: "August",
9: "September",
10: "October",
11: "November",
12: "December",
}
keys := make([]int, 0, len(months))
for k := range months {
keys = append(keys, k)
}
sort.Ints(keys)
fmt.Printf("keys: %+v\n", keys)
for _, k := range keys {
fmt.Println(k, months[k])
}
}
// section: code
/*
// section: output
keys: [1 2 3 4 5 6 7 8 9 10 11 12]
1 January
2 February
3 March
4 April
5 May
6 June
7 July
8 August
9 September
10 October
11 November
12 December
// section: output
*/Sorting By Value
Sorting by value is a little more complex. The easiest way is to invert the map so you have the values as keys, and then do normal pattern of sorting by key.
func main() {
words := map[int]string{
0: "time",
1: "person",
2: "year",
3: "way",
4: "day",
5: "thing",
6: "man",
7: "world",
8: "life",
9: "hand",
10: "part",
12: "child",
13: "eye",
14: "woman",
15: "place",
16: "work",
17: "week",
18: "case",
19: "point",
20: "government",
21: "company",
22: "number",
23: "group",
24: "problem",
25: "fact",
}
// create an inverted map where values are now the keys
sorted := make(map[string]int)
keys := make([]string, 0, len(words))
for k, v := range words {
keys = append(keys, v)
sorted[v] = k
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(sorted[k], k)
}
}
package main
import (
"fmt"
"sort"
)
// section: code
func main() {
words := map[int]string{
0: "time",
1: "person",
2: "year",
3: "way",
4: "day",
5: "thing",
6: "man",
7: "world",
8: "life",
9: "hand",
10: "part",
12: "child",
13: "eye",
14: "woman",
15: "place",
16: "work",
17: "week",
18: "case",
19: "point",
20: "government",
21: "company",
22: "number",
23: "group",
24: "problem",
25: "fact",
}
// create an inverted map where values are now the keys
sorted := make(map[string]int)
keys := make([]string, 0, len(words))
for k, v := range words {
keys = append(keys, v)
sorted[v] = k
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(sorted[k], k)
}
}
// section: code
/*
// section: output
18 case
12 child
21 company
4 day
13 eye
25 fact
20 government
23 group
9 hand
8 life
6 man
22 number
10 part
1 person
15 place
19 point
24 problem
5 thing
0 time
3 way
17 week
14 woman
16 work
7 world
2 year
// section: output
*/And now you can use the sorted slice you created to iterate back through the map in the order you created:
18 case
12 child
21 company
4 day
13 eye
25 fact
20 government
23 group
9 hand
8 life
6 man
22 number
10 part
1 person
15 place
19 point
24 problem
5 thing
0 time
3 way
17 week
14 woman
16 work
7 world
2 yearpackage main
import (
"fmt"
"sort"
)
// section: code
func main() {
words := map[int]string{
0: "time",
1: "person",
2: "year",
3: "way",
4: "day",
5: "thing",
6: "man",
7: "world",
8: "life",
9: "hand",
10: "part",
12: "child",
13: "eye",
14: "woman",
15: "place",
16: "work",
17: "week",
18: "case",
19: "point",
20: "government",
21: "company",
22: "number",
23: "group",
24: "problem",
25: "fact",
}
// create an inverted map where values are now the keys
sorted := make(map[string]int)
keys := make([]string, 0, len(words))
for k, v := range words {
keys = append(keys, v)
sorted[v] = k
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(sorted[k], k)
}
}
// section: code
/*
// section: output
18 case
12 child
21 company
4 day
13 eye
25 fact
20 government
23 group
9 hand
8 life
6 man
22 number
10 part
1 person
15 place
19 point
24 problem
5 thing
0 time
3 way
17 week
14 woman
16 work
7 world
2 year
// section: output
*/Maps And Concurrency
Maps are NOT concurrent safe! This is important to know, as it can be the cause of many bugs.
package main
func main() {
numbers := map[int]int{}
for i := 0; true; i++ {
go func(i int) {
numbers[i] = i
}(i)
}
}package main
func main() {
numbers := map[int]int{}
for i := 0; true; i++ {
go func(i int) {
numbers[i] = i
}(i)
}
}goroutine 6906 [runnable]:
main.main.func1(0xc420014180, 0xc42000e0d8)
./src/maps-threaded.go:6
created by main.main
./src/maps-threaded.go:8 +0x9e
The topic of maps and concurrency, and how to protect maps, will be covered in a different module.
For now it is best to remember that maps are NOT concurrent safe.
NOTE: As of Go 1.9, there is now a sync.Map available which in some cases may significantly reduce lock contention compared to a Go map paired with a separate Mutex or RWMutex.
Reference Articles
- For a deep dive into how Go implements maps, read Dave Cheney’s map articles.
- Go Maps in Action
- Effective Go: Maps
Exercise (15 Mins)
package main
func main() {
beatles := map[string]string{}
beatles["John"] = "guitar"
beatles["Paul"] = "bass"
beatles["George"] = "guitar"
beatles["Ringo"] = "drums"
// Loop through the map and print out the key and value
// Look up the key `Bob` and detect that it wasn't found by printing `not found`
// Declare a slice of strings called `keys` and collect all the keys from the map.
// sort the keys
// print out the keys
// print out the sorted map
}package main
func main() {
beatles := map[string]string{}
beatles["John"] = "guitar"
beatles["Paul"] = "bass"
beatles["George"] = "guitar"
beatles["Ringo"] = "drums"
// Loop through the map and print out the key and value
// Look up the key `Bob` and detect that it wasn't found by printing `not found`
// Declare a slice of strings called `keys` and collect all the keys from the map.
// sort the keys
// print out the keys
// print out the sorted map
}Solution
package main
import (
"fmt"
"sort"
)
func main() {
beatles := map[string]string{}
beatles["John"] = "guitar"
beatles["Paul"] = "bass"
beatles["George"] = "guitar"
beatles["Ringo"] = "drums"
// Loop through the map and print out the key and value
for key, value := range beatles {
fmt.Printf("key: %q, value: %q\n", key, value)
}
// Look up the key `Bob` and detect that it wasn't found by printing `not found`
if bob, ok := beatles["Bob"]; !ok {
fmt.Println("not found")
} else {
fmt.Println(bob)
}
// Declare a slice of strings called `keys` and collect all the keys from the map.
keys := []string{}
for k := range beatles {
keys = append(keys, k)
}
// sort the keys
sort.Strings(keys)
// print out the keys
fmt.Println(keys)
// print out the sorted map
for _, k := range keys {
fmt.Println(k, beatles[k])
}
}
// section: main
package main
import (
"fmt"
"sort"
)
func main() {
beatles := map[string]string{}
beatles["John"] = "guitar"
beatles["Paul"] = "bass"
beatles["George"] = "guitar"
beatles["Ringo"] = "drums"
// Loop through the map and print out the key and value
for key, value := range beatles {
fmt.Printf("key: %q, value: %q\n", key, value)
}
// Look up the key `Bob` and detect that it wasn't found by printing `not found`
if bob, ok := beatles["Bob"]; !ok {
fmt.Println("not found")
} else {
fmt.Println(bob)
}
// Declare a slice of strings called `keys` and collect all the keys from the map.
keys := []string{}
for k := range beatles {
keys = append(keys, k)
}
// sort the keys
sort.Strings(keys)
// print out the keys
fmt.Println(keys)
// print out the sorted map
for _, k := range keys {
fmt.Println(k, beatles[k])
}
}
// section: main
/*
// section: output
key: "John", value: "guitar"
key: "Paul", value: "bass"
key: "George", value: "guitar"
key: "Ringo", value: "drums"
not found
[George John Paul Ringo]
George guitar
John guitar
Paul bass
Ringo drums
// section: output
*/Output:
key: "John", value: "guitar"
key: "Paul", value: "bass"
key: "George", value: "guitar"
key: "Ringo", value: "drums"
not found
[George John Paul Ringo]
George guitar
John guitar
Paul bass
Ringo drums// section: main
package main
import (
"fmt"
"sort"
)
func main() {
beatles := map[string]string{}
beatles["John"] = "guitar"
beatles["Paul"] = "bass"
beatles["George"] = "guitar"
beatles["Ringo"] = "drums"
// Loop through the map and print out the key and value
for key, value := range beatles {
fmt.Printf("key: %q, value: %q\n", key, value)
}
// Look up the key `Bob` and detect that it wasn't found by printing `not found`
if bob, ok := beatles["Bob"]; !ok {
fmt.Println("not found")
} else {
fmt.Println(bob)
}
// Declare a slice of strings called `keys` and collect all the keys from the map.
keys := []string{}
for k := range beatles {
keys = append(keys, k)
}
// sort the keys
sort.Strings(keys)
// print out the keys
fmt.Println(keys)
// print out the sorted map
for _, k := range keys {
fmt.Println(k, beatles[k])
}
}
// section: main
/*
// section: output
key: "John", value: "guitar"
key: "Paul", value: "bass"
key: "George", value: "guitar"
key: "Ringo", value: "drums"
not found
[George John Paul Ringo]
George guitar
John guitar
Paul bass
Ringo drums
// section: output
*/Stretch Exercise
During a data import, the first and last names were incorrectly combined.
map[1:{1 Cory Bates} 2:{2 Mark Raymond} 3:{3 Tim LaNou}]
Using your knowledge of maps, write the code to manually correct the names in the map.
package main
import "fmt"
type User struct {
ID int
Name string
}
func main() {
users := make(map[int]User)
u := User{ID: 1, Name: "Cory Bates"}
users[u.ID] = u
u = User{ID: 2, Name: "Mark Raymond"}
users[u.ID] = u
u = User{ID: 3, Name: "Tim LaNou"}
users[u.ID] = u
// Look up Cory
// Update Cory's name to "Cory LaNou"
// Set the user of ID `1` to the updated user
// Fix the remaining users names:
// ID:2, Mark -> "Mark Bates"
// ID:3, Tim -> "Tim Raymond"
fmt.Println(users)
}package main
import "fmt"
type User struct {
ID int
Name string
}
func main() {
users := make(map[int]User)
u := User{ID: 1, Name: "Cory Bates"}
users[u.ID] = u
u = User{ID: 2, Name: "Mark Raymond"}
users[u.ID] = u
u = User{ID: 3, Name: "Tim LaNou"}
users[u.ID] = u
// Look up Cory
// Update Cory's name to "Cory LaNou"
// Set the user of ID `1` to the updated user
// Fix the remaining users names:
// ID:2, Mark -> "Mark Bates"
// ID:3, Tim -> "Tim Raymond"
fmt.Println(users)
}Solution
package main
import "fmt"
type User struct {
ID int
Name string
}
func main() {
users := make(map[int]User)
u := User{ID: 1, Name: "Cory Bates"}
users[u.ID] = u
u = User{ID: 2, Name: "Mark Raymond"}
users[u.ID] = u
u = User{ID: 3, Name: "Tim LaNou"}
users[u.ID] = u
// Look up Cory
u = users[1]
// Update Cory's name to "Cory LaNou"
u.Name = "Cory LaNou"
// Set the user of ID `1` to the updated user
users[u.ID] = u
// Fix the remaining users names:
// ID:2, Mark -> "Mark Bates"
// ID:3, Tim -> "Tim Raymond"
u = users[2]
u.Name = "Mark Bates"
users[u.ID] = u
u = users[3]
u.Name = "Tim Raymond"
users[u.ID] = u
fmt.Println(users)
}package main
import "fmt"
type User struct {
ID int
Name string
}
func main() {
users := make(map[int]User)
u := User{ID: 1, Name: "Cory Bates"}
users[u.ID] = u
u = User{ID: 2, Name: "Mark Raymond"}
users[u.ID] = u
u = User{ID: 3, Name: "Tim LaNou"}
users[u.ID] = u
// Look up Cory
u = users[1]
// Update Cory's name to "Cory LaNou"
u.Name = "Cory LaNou"
// Set the user of ID `1` to the updated user
users[u.ID] = u
// Fix the remaining users names:
// ID:2, Mark -> "Mark Bates"
// ID:3, Tim -> "Tim Raymond"
u = users[2]
u.Name = "Mark Bates"
users[u.ID] = u
u = users[3]
u.Name = "Tim Raymond"
users[u.ID] = u
fmt.Println(users)
}