Maps - Practical Go Lessons-22
Maps - Practical Go Lessons-22
com/chap-22-maps
• Key-Value pair
• Map entry
• Hash table
• Time complexity
1 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
// maps/without-maps/main.go
package main
import "fmt"
func main() {
results := []testScore{
{"John Doe", 20},
{"Patrick Indexing", 15},
//...
//...
{"Bob Ferring", 7},
{"Claire Novalingua", 8},
}
fmt.Println(results)
}
We have a type struct testScore and a slice results composed of testScore s elements. Now let’s imagine that I want to retrieve the
score of the student named Claire Novalingua.
We are using a slice we have to iterate over each element to find the item searched :
• We have to iterate potentially over all elements of the slice. Imagine that your slice contains thousands of elements! The impact on
performance can be important.
• The code written is not short. We use a for loop range and a nested comparison. Those five lines are not easy to read.
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
4 What is a map?
A map is an unordered collection of elements of type T that are indexed by unique keys of type U1.
4.0.0.1 Example :
2 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
Map example[fig:Map-example]
In the previous figure (1) we have a map representing the football world cup winners by year. Here the key is the year (which is an uint8 )
and the values that represent the country name of the winner ( string ). The map type is denoted :
map[uint8]string
An element of a map is called a “map entry”. It’s also usually named a key-value pair.
Let’s take another example; a dictionary can be stored using a map. In a dictionary, we have definitions of words that are stored. In this
case, the definitions are the elements, and the words represent the keys. When you use a dictionary, you search for a specific word to get
its definition. We never look into a dictionary by definition. This type of lookup might cost you a lot of time because definitions are not
indexed. We can keep this analogy for maps. We always make a lookup based on a specific key! Maps are indexed by keys.
Can we put all types defined in Go for the key type? And for the value type?
• function
• map
• slice
3 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
If we use an image, a map is like a corridor with locked doors. Behind each door, there is a value. The keys that can open the doors are
unique (you can make copies of the keys, but the keys’ design stays the same). Each key opens a given door. There is a 1-1 relation
between the keys and the doors.
7 Elements
The elements are what you store on the map. For the elements, there are no restrictions concerning the type. You can store whatever you
want. You can also store another map into a value.
For instance, an element can be a year, the score of a match, a type struct representing a user of an application...
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
m:=make(map[string]int)
m will be a value of type map[string]int . This is called a map value, and internally it’s a pointer to a hash table. We will see in the next
sections what is exactly a hash table, so do not worry now about it.
worldCupWinners := map[int]string{
1930: "Uruguay",
1934: "Italy",
1938: "Italy",
1950: "Uruguay"}
fmt.Println(worldCupWinners)
//map[1930:Uruguay 1934:Italy 1938:Italy 1950:Uruguay]
In the previous code listing, we create a map named worldCupWinners . This map is directly populated with four entries. The first four
winners of the football world cup. The keys here are integers; they represent the years. The values are strings that represents the
country’s name that won the cup in the given year. In 1930 it was Uruguay that won the cup.
Please note that values can be repeated. The value Italy and Uruguay are repeated twice. It’s perfectly authorized.
Note also that after initializing a map, you can add new values to it. In our example, we can add another year to the map!
You can also use the map literal syntax to create an empty map.
a := map[int]string{}
4 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
In the previous code listing, a is a map (initialized and allocated), but no key-value pairs are stored in it.
Hash Table
• A hash function. its role is to transform a key into a unique identifier. For instance, the key 1930 will be passed to the hash function,
and it will return “4”.
• An indexed storage that is used to keep the values in memory. The storage is eventually organized in buckets. Each bucket can store
a specific number of values.
When we add a key-value pair to the hash table, the algorithm will go through the following steps :
1. From the key get the return value of hash_function(key) (we denote the return value h ). h is the index where data is stored
(for instance, 4)
Retrieving a value from a given key will also make use of the hash function :
1. From the value get the return value hash_function(key) . It will return the container index.
2. Extract the data from the given container and return it to the user.
◦ if you pass the key 1989 to the hash function, it will return, for instance i .
i will be the index of the storage of the value linked to 1989 .
◦ Imagine now that for 1938 the hash function returns the same index i !
◦ When you store something with the key 1989 it will erase what is already stored for the key 1938 .
◦ Imagine the mess that such collisions can produce! For instance, the hash function MD5 can produce collisions. (for more
information, read the article [@stevens2006fast])
• Compute an index to get the location of the data in a limited amount of time. (the hash function must be time-efficient)
5 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
• The hash produced must be stable in time. The key should produce the same hash at each call.
• The time complexity is a kind of complexity; it designates the amount of computer time needed to run a program4
Time complexity will depend on the hash table’s implementation, but keep in mind that time complexity is very low for searching a value
and inserting a new key-value pair.
• Insertion : O(1)
• Search : O(1)
Search and insertion will take the same number of basic operations on a map containing three elements and a map containing 3 million
elements!
We say that it’s a constant-time algorithm. We also say that it’s order 1.I used here the Big-O notation5.
◦ Key: 1930
• The key will be passed to the hash function it will return an hash value(which is an integer)
• This hash value contains the bucket id. The hash function does not directly return the id of the bucket, the return value h has to be
transformed to get the bucket id.
◦ Bucket id = 3
• Knowing the bucket id, the next step is to find the correct entry in the bucket. This is done by comparing the key given to all the
bucket keys.
◦ Key : “1930”. Go will iterate through the keys of the bucket and return the corresponding element
• Go will then iterate over the bucket elements to find a place to store the key and the element.
◦ When the key is already present, Go will override the element’s value.
6 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
Go Hashmap implementation
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
• In the alpha version, we will load the list of employees via a CSV file
• The users will need to query employees by their employeeId (composed of letters and numbers)
employeeId,employeeName,genre,position
V45657,John Ollivero,M,CEO
V45658,Frane Elindo,F,CTO
V6555,Walter Van Der Bolstenberg,M,Sales Manager
7 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
// maps/reading-csv/main.go
package main
import (
"encoding/csv"
"fmt"
"io"
"log"
"os"
)
func main() {
file, err := os.Open("/Users/maximilienandile/Documents/DEV/goBook/maps/usages/employees.csv")
if err != nil {
log.Fatalf("impossible to open file %s", err)
}
defer file.Close()
r := csv.NewReader(file)
for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Println(record)
}
}
We are using the standard library os . Like always, we check for errors and return if they are some (but before returning, we are printing an
error message).
After that, we use the csv package. We create a reader with r := csv.NewReader(file) , that will allow us to read the file line by line. We
initialize a line counter to keep track of the line number.
Then we start the reading with the for loop. We read a new line with the record, err := r.Read() . The record variable is a slice of strings
( []string ). Next, we check for errors, with the subtility that r.Read() will populate err with io.EOF when it has reached the end of the
file. We have to check that before checking that err is not nil . If we have reached the end of the file, we will stop the for loop with the
keyword break . After that, we can finally read the data of the file.
The variable record will return, for instance [V45657 John Ollivero M CEO] .
The data is stored in a slice, and at the index 0, we will find the employeeID , at index one the name, at index two the genre, and the
position at index 3 !
The preparatory work is done let’s jump to the map creation and usage
8 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
To add a pair composed of a key and a element simply use the following syntax :
m[key] = value
14 Retrieve a value
To get an element from a map you have to know it’s key. They are two different ways to do it :
walter := employees["V6555"]
Here we assign to the variable walter the value contained into the map employeeMap with the key V6555.
Here we attempt to get the value of the employee that has the id "ABC55555" .
The key does not exist on the map. Go will return the null value of the type.
14.0.2.1 Warning! Be very careful with this syntax because it can lead to errors.
In the case of our HR software example, imagine that after loading the data into the map, you propose to your users some kind of interface
where they can see the data of an employee in function of its id. What if the user types the id “100”. You implement a function that will
return an employee given a specific key. You will return an empty object employee.
We can guess that the employee does not exist, but it’s not 100% sure. Those empty fields can also come from a corrupted file.
That’s why Go creators have provided a more clever way to retrieve an entry in a map.
v, ok := myMap[k]
The variable ok is a boolean that will hold the indication of the existence of the key-value pair in the map:
the key-value pair exists in the map, v is populated with the value at key k
the key-value pair does not exist, v is populated with the null value of type valueType
9 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
It’s possible to ignore the value if you just want to test the presence of a key into the map :
In the previous example, we are telling the compiler that we do not need the value retrieved by using the underscore (_) character in the
assignation.
// shorter code
if _, ok := employees["ABC4"]; ok {
// the key-element pair exists in the map
} else {
fmt.Println("No employee with ID 'ABC4'")
}
The two values assignment and the ok value check are done in one line!
Why this behavior? Because Go can change the memory location of a key-value pair when it adds a new key-value pair. Go will do this
under the hood to keep the complexity of retrieving a key-value pair at a constant level. As a consequence, the address can become invalid.
Go prefers to forbid the access of a possible invalid address than letting you try your chance. This is a good thing !
The garbage collector will not do its job and remove the unused memory.
15 Delete an entry
You can delete a key-value pair by using the delete built-in function. The function has the following header :
It takes:
• a key
The second argument is the key of the entry, you want to destroy.
10 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
• If the entry does not exist in the map it will not panic (and it will compile).
• If you use for the second argument a different type than the key type, the program will not compile.
If you want to delete the entry with index two from the map employees you can use the following code :
delete(employees, "ABC4")
The entry with the key "ABC4" will be destroyed from memory if it exists.
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
16 Length
You can retrieve the number of entries in the map with the built-in len :
fmt.Println(len(employees))
// 3
// There are three entries into the map
fmt.Println(len(employeeMap))
// 2
// There are two entries into the map
This is because order is not assured. If we try to run a second time the same script, we might have the following result :
order := []string{}
order = append(order, employeeID)
employeeMap[employeeID] = employee
Here we create a slice order. This slice will store the keys in the insertion order into the map. So each time we add an entry to the map, we
add the key to the slice by calling order = append(order, employeeID) .
11 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
We iterate over the slice order to get the keys, and then we retrieve the entry value by calling employees[k] , where k represents a key of
the map employees .
The key is the enployeeID and the value is a structtype employee . But imagine that we do not want to store a struct but another map
instead :
Two-dimensional map[fig:Two-dimensional-map]
In the figure 2 there are two maps. The second map is of type map[string]string . We store as keys “Name”, “Position” and “Genre” and
the values are the corresponding employee data. The first map is of type map[int]map[string]string . The type notation is a little bit
confusing, but when you look at it closely it makes sense :
The second map is the inner map. It is the value of the first map. Each entry of this type has an integer key and for value a
map[string]string.
Two-dimensional maps are, in my opinion, too complicated. You might better use a map with a struct value.
12 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
19 Test Yourself
19.1 Questions
1. How to check if a key/element pair is in a map?
5. When you iterate over a map, then the runtime will return the keys and the elements in the order you inserted them. True or False ?
9. If a map M does not contain the key K, what will return M[K]?
19.2 Answers
1. How to check if a key/element pair is in a map?
1. Let’s say that you want to check if there is a key/element pair with a key equal to 102 in a map rooms: room, ok =
rooms[102]; . When ok is true, the pair exists.
2. How are Go maps implemented internally?
1. Because the comparison operators == and =* are not fully defined for those types. Go needs to be able to compare
keys in its internal implementation. \end{enumerate} \item When you iterate over a map, then the runtime will return
the keys and the elements in the order you inserted them. True or False ? \begin{enumerate} \item False. A map is an
unordered collection. Go will \textbf{not} keep the memory of the insertion order. You will have to save it yourself
if you need it. \end{enumerate} \item How to remove a key/element pair from a map? \lstinline{delete(employees,
"ABC4")} \begin{enumerate} \item When the element is not found, nothing will happen
5. How to get the number of key/element pairs in a map?
len(myMap)
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
20 Key Takeways
• A map is an unordered collection of elements (values) of type V that are indexed by unique keys of type K
13 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
m := make(map[string]uint8)
var m map[string]uint8
log.Println(m)
◦ When no value is found, the variable valueRetrieved will be equal to the zero value of the map value type.
} else {
// not found :(
ok is a boolean which is equal to true if an entry with that key exists in the map
• You can iterate over a map with a for loop (with range clause)
◦ Warning: the order of insertion may not be used (it is not guaranteed)!
◦ To keep in memory the order of insertion in the map, create a slice and append each key to it
◦ Then you can iterate over the slice and fetch each value in the order of insertion.
• Insertion and lookup in a map are very quick, even if the map has many entries.
1. https://golang.org/ref/spec#Map_types↩
2. Go Specs https://golang.org/ref/spec#Map_types↩
3. https://en.wikipedia.org/wiki/Computational_complexity↩
4. https://en.wikipedia.org/wiki/Time_complexity↩
5. You can find more info about this notation on this Wikipedia article: https://en.wikipedia.org/wiki/Big_O_notation↩
Bibliography
• [stevens2006fast] Stevens, Marc. 2006. “Fast Collision Attack on Md5.” IACR Cryptology ePrint Archive 2006: 104.
Previous Next
Errors
14 of 15 02/01/2023, 02:13
Maps - Practical Go Lessons https://www.practical-go-lessons.com/chap-22-maps
Slices
Table of contents
Did you spot an error ? Want to give me feedback ? Here is the feedback page! ×
Newsletter:
Like what you read ? Subscribe to the newsletter.
Practical Go Lessons
By Maximilien Andile
Copyright (c) 2023
Follow me Contents
Posts
Book
Support the author Video Tutorial
About
The author
Legal Notice
Feedback
Buy paper or digital copy
Terms and Conditions
15 of 15 02/01/2023, 02:13