What a map is
Go's dictionary type is the map. It stores key-value pairs, and each key maps to one value. Maps are built into the language, so they need no import. The type is written map[KeyType]ValueType: in map[string]int, keys are strings and values are ints.
Two ways to create one
Start empty and add entries by assigning to keys:
scores := map[string]int{}
scores["Alice"] = 95
scores["Bob"] = 87
scores["Charlie"] = 72
Or use a map literal, which creates the map and fills it in one statement. Each entry is key: value, and in a multi-line literal every line ends with a comma, including the last:
ages := map[string]int{
"Alice": 25,
"Bob": 30,
}
fmt.Println(ages["Alice"]) // 25
fmt.Println(len(ages)) // 2
The empty braces in map[string]int{} make an empty but ready-to-use map. make(map[string]int) does the same. A map that is only declared with var is nil, and assigning into it panics, so use one of the creating forms.
What you can do with a map
Creating the map is the first step. The next lessons cover the everyday operations, and here is a quick preview of them:
m[key]reads a value. A missing key gives the zero value of the value type, soscores["Zed"]is0.m[key] = valueadds a new entry or overwrites an existing one.len(m)is the number of entries.delete(m, key)removes an entry. Deleting a key that isn't there does nothing.for key, value := range mvisits every entry.
Because lookups go straight to the key instead of scanning, a map is the right tool whenever you want to find something by name or id, such as the score for a given student.
Choosing key and value types
Both types are your choice. A map from ids to names is map[int]string, and a map from a name to several scores is map[string][]int:
byID := map[int]string{1: "one", 2: "two"}
fmt.Println(byID[2]) // two
grades := map[string][]int{"Alice": {90, 100}}
fmt.Println(grades) // map[Alice:[90 100]]
The key type is fixed for the whole map, and so is the value type. You can't mix a string key and an int key in one map[string]int.
Maps are shared, not copied
A map variable refers to the data, much as a slice does. Assigning it to another variable gives you two names for one map:
other := scores
other["Alice"] = 0
fmt.Println(scores) // map[Alice:0 Bob:87 Charlie:72]
The change made through other shows up in scores. To get an independent copy you have to create a second map and copy the entries over yourself.
The exercise
The task is to create a map named scores with three students: Alice with 95, Bob with 87 and Charlie with 72. The lesson has one step, and the literal form does it in a single line, with the keys as strings and the scores as ints.
The solution in Go
scores := map[string]int{"Alice": 95, "Bob": 87, "Charlie": 72}
This is the same map the first example builds with three assignments. The literal is shorter and creates the map with its contents already in place.
Things to know
- Use
:=inside functions. The short declaration works only inside a function body, which is where the lesson's code lives. - Keys are unique. Assigning to an existing key replaces its value. In a literal, repeating a constant key is a compile error.
- The order is not fixed. Printing a map with
fmt.Printlnshows keys in sorted order, but arangeloop visits them in an unspecified order that can change from run to run. - Keys must be comparable. Strings, numbers and booleans work as keys. Slices don't.
- Declaring is not the same as creating.
var scores map[string]intgives a nil map. Reading it is harmless, but the first assignment panics withassignment to entry in nil map. The literal in the solution avoids that. - Types must match. With a
map[string]int, a key like1or a value like"high"is a compile error, which catches typos early.