From charlesreid1

Go maps are data structures that use a hash table under the hood to provide fast, O(1) lookup of key-value pairs.

Basics

Initialization

To declare a map type, use the notation:

map[KeyType]ValueType

Example: declare a variable that is a map of string keys to integer values:

var m map[string]int

Like pointers/slices, map types are reference types, so if they are not initialized with a value, they have a nil value. A nil map has not been initialized and so has no underlying space in memory, so you cannot write to a nil map.

To initialize space for a map:

m = make(map[string]int)

The make function allocates space in memory for the hash table. It returns a map value pointing to this location in memory. It is important to note that the actual implementation of a map is not specified by the language, so it depends on the implementation of the Go runtime being used.

To initialize with literal data, use brackets:

commits := map[string]int{
  "asdf": 10,
  "qwerty": 20,
}

This also gives a shorter way of creating maps than the make function:

commits := map[string]int{}

Working with maps

To set a key value pair:

m["route"] = 66

To look up a key and assign the value to a variable:

i := m["route"]

Unlike Python, if a key does not exist, Go will return the value type's zero value. For integers, the value is 0; for strings, empty string; for booleans, False; etc.

j := m["root"]
// j == 0

Two useful built-in functions are length (len) and delete (delete):

// number of items in a map
n := len(m)

// remove the specified key from the map
delete(m, "route")

Like Pyhton, Go supports the two-value assignment format:

i, ok := m["route"]

i is the value in the map if the key exists (zero value otherwise), ok is a boolean indicating whether the key is in the map or not.

Also like Python, you can use underscore to skip assignment of one of the multi-assignment values. To check for existence of a key:

_, ok := m["root"]

Iteration

To iterate over a map, use the range keyword, like with lists:

for key, value := range m {
    fmt.Println(key, ":", value)
}

Sorted Ordered Iteration

Suppose you want to iterate over a map, but you want to do it in order of sorted keys. To do this, we can do the following:

  • Create an array of keys
  • Iterate over the map, adding each key to the array of keys
  • Sort the keys
  • Loop over the sorted keys, and access each value in the order of the specified keys

Implementation Details

Maps may have any key types that are comparable. This excludes slices, maps, and functions, which are not comparable using ==.

That leaves you with boolean, numeric, string, pointer, channel, and interface types, and structs or arrays that contain only those types.

It is possible to use structs as keys. For example, to index page hits by page address and visitor location (e.g., "/home" and "au"), a data structure using page address as key and a second map of country codes to hits as the value would be problematic and unwieldy (inner map would need existence checks). Instead, use a single map, and use a struct for a key:

type Key struct {
    Path, Country string
}
hits := make(map[Key]int)

Now you can access items via, for example, hits[Key{"/home","au"}]

Flags