Golang Maps Tutorial

Welcome to tutorial no. 13 in Golang tutorial series.

What is a map?

A map is a inbuilt data type in Go which is used to store key-value pairs. A practical use case for a map is for storing the currency codes and the corresponding currency names.

USD - United States Dollar
EUR - Euro
INR - India Rupee

A map will be a perfect fit for the above use case use case. The currency code can be the key and the currency name 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 data 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)
currencyCode := make(map[string]string)

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

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

Run in Playground

The program above creates a map named currencyCode with string key and string 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 currency codes and currency names to the currencyCode map.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	currencyCode := make(map[string]string)
 9	currencyCode["USD"] = "US Dollar"
10	currencyCode["GBP"] = "Pound Sterling"
11	currencyCode["EUR"] = "Euro"
12	currencyCode["INR"] = "Indian Rupee"
13	fmt.Println("currencyCode map contents:", currencyCode)
14}

Run in playground

We have added 4 currencyCodes namely USD, GBP, EUR, INR and their corresponding names.

The above program prints,

currencyCode map contents: map[EUR:Euro GBP:Pound Sterling INR:Indian Rupee USD:US Dollar]

As you might have recognized from the above output, the order of the retrieval of values from a map is not guaranteed to be the same as the order in which the elements were added to the map.

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

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	currencyCode := map[string]string {
 9		"USD": "US Dollar",
10		"GBP": "Pound Sterling",
11		"EUR": "Euro",
12	}
13	currencyCode["INR"] = "Indian Rupee"
14	fmt.Println("currencyCode map contents:", currencyCode)
15}

Run in playground

The above program declares currencyCode map and adds 3 items to it during the declaration itself. Later one more element with key INR is added. The program prints

currencyCode map contents: map[EUR:Euro GBP:Pound Sterling INR:Indian Rupee USD:US Dollar]

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 https://go.dev/ref/spec#Comparison_operators

nil map panics

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 currencyCode map[string]string
5	currencyCode["USD"] = "US Dollar"
6}

Run in playground

In the above program, employeeSalary is nil and we are trying to add a new key to a nil 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	currencyCode := map[string]string{
 9		"USD": "US Dollar",
10		"GBP": "Pound Sterling",
11		"EUR": "Euro",
12	}
13	currency := "USD"
14	currencyName := currencyCode[currency]
15	fmt.Println("Currency name for currency code", currency, "is", currencyName)
16}

Run in playground

The above program is pretty straightforward. The currency name for the currency code USD is retrieved and printed. The program prints

Currency name for currency code USD is US Dollar

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 currencyCode map, if we try to access an item which is not present, the zero value of string, “"(the empty string) is returned.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	currencyCode := map[string]string{
 9		"USD": "US Dollar",
10		"GBP": "Pound Sterling",
11		"EUR": "Euro",
12	}
13	fmt.Println("Currency name for currency code INR is", currencyCode["INR"])
14}

Run in playground

The output of the above program is

Currency name for currency code INR is 

The above program returns empty string as the currency name for INR. 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 a currency code key is present in the currencyCode map. The following syntax is used to find out whether a particular key is present in a map.

value, ok := map[key]

ok in the above line of code will be true when the key is present and the value for the key is present in the variable value. If the key is not present, ok will be false and the zero value is returned for value.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7func main() {
 8	currencyCode := map[string]string{
 9		"USD": "US Dollar",
10		"GBP": "Pound Sterling",
11		"EUR": "Euro",
12	}
13	cyCode := "INR"
14	if currencyName, ok := currencyCode[cyCode]; ok {
15		fmt.Println("Currency name for currency code", cyCode, " is", currencyName)
16		return
17	}
18	fmt.Println("Currency name for currency code", cyCode, "not found")
19}

Run in playground

In the above program, in line no. 14, ok will be false since INR key is not present. Hence the program will print,

Currency name for currency code INR 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	currencyCode := map[string]string{
 9		"USD": "US Dollar",
10		"GBP": "Pound Sterling",
11		"EUR": "Euro",
12	}
13	for code, name := range currencyCode {
14		fmt.Printf("Currency Name for currency code %s is %s\n", code, name)
15	}
16}

Run in playground

The above program outputs,

Currency Name for currency code GBP is Pound Sterling
Currency Name for currency code EUR is Euro
Currency Name for currency code USD is US Dollar

One important fact to note is 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	currencyCode := map[string]string{
 9		"USD": "US Dollar",
10		"GBP": "Pound Sterling",
11		"EUR": "Euro",
12	}
13	fmt.Println("map before deletion", currencyCode)
14	delete(currencyCode, "EUR")
15	fmt.Println("map after deletion", currencyCode)
16}

Run in playground

The above program deletes the key EUR and it prints

map before deletion map[EUR:Euro GBP:Pound Sterling USD:US Dollar]
map after deletion map[GBP:Pound Sterling USD:US Dollar]

Even 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 currency name in the map. Wouldn’t it be nice if we are able to store the symbol of the currency too? This can be achieved by using a map of structs. The currency can be represented as a struct containing the fields currency name and currency symbol. This struct value can be stored in the map with a currency code as key . Let’s write a program to understand how this can be done.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7type currency struct {
 8	name   string
 9	symbol string
10}
11
12func main() {
13	curUSD := currency{
14		name:   "US Dollar",
15		symbol: "$",
16	}
17	curGBP := currency{
18		name:   "Pound Sterling",
19		symbol: "£",
20	}
21	curEUR := currency{
22		name:   "Euro",
23		symbol: "€",
24	}
25
26	currencyCode := map[string]currency{
27		"USD": curUSD,
28		"GBP": curGBP,
29		"EUR": curEUR,
30	}
31
32	for cyCode, cyInfo := range currencyCode {
33		fmt.Printf("Currency Code: %s, Name: %s, Symbol: %s\n", cyCode, cyInfo.name, cyInfo.symbol)
34	}
35
36}

Run in playground

In the above program, currency struct contains fields name and symbol. We create three currencies curUSD, curGBP and curEUR.

In line no. 26, we initialize a map with string key and value of type currency with the three currencies we created.

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

Currency Code: USD, Name: US Dollar, Symbol: $
Currency Code: GBP, Name: Pound Sterling, Symbol: £
Currency Code: EUR, Name: Euro, Symbol: €

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	currencyCode := map[string]string{
 9		"USD": "US Dollar",
10		"GBP": "Pound Sterling",
11		"EUR": "Euro",
12	}
13	fmt.Println("length is", len(currencyCode))
14
15}

Run in playground

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

length is 3

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 underlying 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 :).

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.

Next tutorial - Strings