Golang Maps Tutorial

Welcome to tutorial no. 13 in Golang tutorial series.

What is a map?

A map is a builtin type in Go that is used to store key-value pairs. Let’s take the example of a startup with a few employees. For simplicity, let’s assume that the first name of all these employees is unique. We are looking for a data structure to store the salary of each employee. A map will be a perfect fit for this use case. The name of the employee can be the key and the salary can be the value. Maps are similar to dictionaries in other languages such as Python.

How to create a map?

A map can be created by passing the type of key and value to the make function. The following is the syntax to create a new map.

make(map[type of key]type of value)
employeeSalary := make(map[string]int)

The above line of code creates a map named employeeSalary which has string keys and int values.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	employeeSalary := make(map[string]int)
 9	fmt.Println(employeeSalary)
10}

Run in Playground

The program above creates a map named employeeSalary with string key and int value. The above program will print,

map[]

Since we have not added any elements to the map, it’s empty.

Adding items to a map

The syntax for adding new items to a map is the same as that of arrays. The program below adds some new employees to the employeeSalary map.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	employeeSalary := make(map[string]int)
 9	employeeSalary["steve"] = 12000
10	employeeSalary["jamie"] = 15000
11	employeeSalary["mike"] = 9000
12	fmt.Println("employeeSalary map contents:", employeeSalary)
13}

Run in playground

We have added three employees steve, jamie and mike and their corresponding salaries.

The above program prints,

employeeSalary map contents: map[steve:12000 jamie:15000 mike:9000]

It is also possible to initialize a map during the declaration itself.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	employeeSalary := map[string]int {
 9		"steve": 12000,
10		"jamie": 15000,
11	}
12	employeeSalary["mike"] = 9000
13	fmt.Println("employeeSalary map contents:", employeeSalary)
14}

Run in playground

The above program declares employeeSalary and adds two elements to it during the declaration itself. Later one more element with key mike is added. The program prints

employeeSalary map contents: map[jamie:15000 mike:9000 steve:12000]

It’s not necessary that only string types should be keys. All comparable types such as boolean, integer, float, complex, string, … can also be keys. Even user-defined types such as structs can be keys. If you would like to know more about comparable types, please visit http://golang.org/ref/spec#Comparison_operators

Zero value of a map

The zero value of a map is nil. If you try to add elements to a nil map, a run-time panic will occur. Hence the map has to be initialized before adding elements.

1package main
2
3func main() {
4	var employeeSalary map[string]int
5	employeeSalary["steve"] = 12000
6}

Run in playground

In the above program, employeeSalary is nil and we are trying to add a new key to the map. The program will panic with error

panic: assignment to entry in nil map

Retrieving value for a key from a map

Now that we have added some elements to the map, let’s learn how to retrieve them. map[key] is the syntax to retrieve elements of a map.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	employeeSalary := map[string]int{
 9		"steve": 12000,
10		"jamie": 15000,
11        "mike": 9000,
12	}
13	employee := "jamie"
14    salary := employeeSalary[employee]
15	fmt.Println("Salary of", employee, "is", salary)
16}

Run in playground

The above program is pretty straightforward. The salary of the employee jamie is retrieved and printed. The program prints

Salary of jamie is 15000

What will happen if an element is not present? The map will return the zero value of the type of that element. In the case of employeeSalary map, if we try to access an element which is not present, the zero value of int which is 0 will be returned.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	employeeSalary := map[string]int{
 9		"steve": 12000,
10		"jamie": 15000,
11	}
12	fmt.Println("Salary of joe is", employeeSalary["joe"])
13}

Run in playground

The output of the above program is

Salary of joe is 0

The above program returns the salary of joe as 0. There will be no runtime error when we try to retrieve the value for a key that is not present in the map.

Checking if a key exists

In the above section we learned that when a key is not present, the zero value of the type will be returned. This doesn’t help when we want to find out whether the key actually exists in the map.

For example, we want to know whether an key is present in the employeeSalary map.

value, ok := map[key]

The above is the syntax to find out whether a particular key is present in a map. If ok is true, then the key is present and its value is present in the variable value, else the key is absent.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	employeeSalary := map[string]int{
 9		"steve": 12000,
10		"jamie": 15000,
11	}
12	newEmp := "joe"
13	value, ok := employeeSalary[newEmp]
14	if ok == true {
15		fmt.Println("Salary of", newEmp, "is", value)
16		return
17	}
18	fmt.Println(newEmp, "not found")
19
20}

Run in playground

In the above program, in line no. 13, ok will be false since joe is not present. Hence the program will print,

joe not found

Iterate over all elements in a map

The range form of the for loop is used to iterate over all elements of a map.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	employeeSalary := map[string]int{
 9		"steve": 12000,
10		"jamie": 15000,
11		"mike":  9000,
12	}
13	fmt.Println("Contents of the map")
14	for key, value := range employeeSalary {
15		fmt.Printf("employeeSalary[%s] = %d\n", key, value)
16	}
17
18}

Run in playground

The above program outputs,

Contents of the map
employeeSalary[mike] = 9000
employeeSalary[steve] = 12000
employeeSalary[jamie] = 15000

One important fact is that the order of the retrieval of values from a map when using for range is not guaranteed to be the same for each execution of the program. It is also not the same as the order in which the elements were added to the map

Deleting items from a map

delete(map, key) is the syntax to delete key from a map. The delete function does not return any value.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	employeeSalary := map[string]int{
 9		"steve": 12000,
10		"jamie": 15000,		
11        "mike": 9000,
12	}
13	fmt.Println("map before deletion", employeeSalary)
14	delete(employeeSalary, "steve")
15	fmt.Println("map after deletion", employeeSalary)
16
17}

Run in playground

The above program deletes the key steve and it prints

map before deletion map[steve:12000 jamie:15000 mike:9000]
map after deletion map[mike:9000 jamie:15000]

If we try to delete a key that is not present in the map, there will be no runtime error.

Map of structs

So far we have only been storing the salary of the employee in the map. Wouldn’t it be nice if we are able to store the country of each employee in the map too? This can be achieved by using a map of structs. The employee can be represented as a struct containing fields salary and country and they will be stored in the map with a string key and struct value. Let’s write a program to understand how this can be done.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7type employee struct {
 8	salary  int
 9	country string
10}
11
12func main() {
13	emp1 := employee{
14		salary:  12000,
15		country: "USA",
16	}
17	emp2 := employee{
18		salary:  14000,
19		country: "Canada",
20	}
21	emp3 := employee{
22		salary:  13000,
23		country: "India",
24	}
25	employeeInfo := map[string]employee{
26		"Steve": emp1,
27		"Jamie": emp2,
28		"Mike":  emp3,
29	}
30
31	for name, info := range employeeInfo {
32		fmt.Printf("Employee: %s Salary:$%d  Country: %s\n", name, info.salary, info.country)
33	}
34
35}

Run in playground

In the above program, employee struct contains fields salary and country. We create three employees emp1, emp2 and emp3.

In line no. 25, we initialize a map with key type string and value type employee with the three employees we created.

The map is iterated in line no. 31 and the employee details are printed in the next line. This program will print,

Employee: Mike Salary:$13000  Country: India
Employee: Steve Salary:$12000  Country: USA
Employee: Jamie Salary:$14000  Country: Canada

Length of the map

Length of the map can be determined using the len function.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	employeeSalary := map[string]int{
 9		"steve": 12000,
10		"jamie": 15000,
11	}
12	fmt.Println("length is", len(employeeSalary))
13
14}

Run in playground

len(employeeSalary) in the above program returns the length of the map. The above program prints,

length is 2

Maps are reference types

Similar to slices, maps are reference types. When a map is assigned to a new variable, they both point to the same internal data structure. Hence changes made in one will reflect in the other.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	employeeSalary := map[string]int{
 9		"steve": 12000,
10		"jamie": 15000,		
11		"mike": 9000,
12	}
13	fmt.Println("Original employee salary", employeeSalary)
14	modified := employeeSalary
15	modified["mike"] = 18000
16	fmt.Println("Employee salary changed", employeeSalary)
17
18}

Run in playground

In line no. 14 of the above program, employeeSalary is assigned to modified. In the next line, the salary of mike is changed to 18000 in the modified map. Mike’s salary will now be 18000 in employeeSalary too. The program outputs,

Original employee salary map[jamie:15000 mike:9000 steve:12000]
Employee salary changed map[jamie:15000 mike:18000 steve:12000]

Similar is the case when maps are passed as parameters to functions. When any change is made to the map inside the function, it will be visible to the caller also.

Maps equality

Maps can’t be compared using the == operator. The == can be only used to check if a map is nil.

 1package main
 2
 3func main() {  
 4    map1 := map[string]int{
 5        "one": 1,
 6        "two": 2,
 7    }
 8
 9    map2 := map1
10
11    if map1 == map2 {
12    }
13}

Run in playground

The above program will fail to compile with error

invalid operation: map1 == map2 (map can only be compared to nil)

One way to check whether two maps are equal is to compare each one’s individual elements one by one. The other way is using reflection. I would encourage you to write a program for this and make it work :).

I have compiled all the concepts we have discussed in a single program. You can download it from github.

This brings us to the end of this tutorial. Hope you enjoyed it. Please leave your feedback and comments. Please consider sharing this tutorial on twitter and LinkedIn. Have a good day.

If you would like to advertise on this website, hire me, or if you have any other software development needs please email me at naveen[at]golangbot[dot]com.

Next tutorial - Strings