Part 13: Maps

Welcome to tutorial no. 13 in Golang tutorial series.

What is a map?

A map is a builtin type in Go which associates a value to a key. The value can be retrieved using the corresponding key.

How to create a map?

A map can be created by passing the type of key and value to the make function. make(map[type of key]type of value) is the syntax to create a map.

personSalary := make(map[string]int)  

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

The zero value of a map is nil. If you try to add items to nil map, a run time panic will occur. Hence the map has to be initialized using make function.

package main

import (  
    "fmt"
)

func main() {  
    var personSalary map[string]int
    if personSalary == nil {
        fmt.Println("map is nil. Going to make one.")
        personSalary = make(map[string]int)
    }
}

In the above program, personSalary is nil and hence it will be initialized using the make function. The program will output map is nil. Going to make one.

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 items to the personSalary map.

package main

import (  
    "fmt"
)

func main() {  
    personSalary := make(map[string]int)
    personSalary["steve"] = 12000
    personSalary["jamie"] = 15000
    personSalary["mike"] = 9000
    fmt.Println("personSalary map contents:", personSalary)
}

The above program outputs, personSalary map contents: map[steve:12000 jamie:15000 mike:9000]

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

package main

import (  
    "fmt"
)

func main() {  
    personSalary := map[string]int {
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println("personSalary map contents:", personSalary)
}

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

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

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. If you would like to know more about comparable types, please visit http://golang.org/ref/spec#Comparison_operators



Accessing items of a map

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

package main

import (  
    "fmt"
)

func main() {  
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    employee := "jamie"
    fmt.Println("Salary of", employee, "is", personSalary[employee])
}

The above program is pretty straightforward .The salary of the employee jamie is retrieved and printed. The program outputs Salary of jamie is 15000.

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

package main

import (  
    "fmt"
)

func main() {  
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    employee := "jamie"
    fmt.Println("Salary of", employee, "is", personSalary[employee])
    fmt.Println("Salary of joe is", personSalary["joe"])
}

Output of the above program is

Salary of jamie is 15000  
Salary of joe is 0  

The above program returns the salary of joe as 0. We did not get any runtime error stating that the key joe is not present in the personSalary map.

What if we want to know whether a key is present in a map or not.

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.

package main

import (  
    "fmt"
)

func main() {  
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    newEmp := "joe"
    value, ok := personSalary[newEmp]
    if ok == true {
        fmt.Println("Salary of", newEmp, "is", value)
    } else {
        fmt.Println(newEmp,"not found")
    }

}

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

joe not found  

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

package main

import (  
    "fmt"
)

func main() {  
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println("All items of a map")
    for key, value := range personSalary {
        fmt.Printf("personSalary[%s] = %d\n", key, value)
    }

}

The above program outputs,

All items of a map  
personSalary[mike] = 9000  
personSalary[steve] = 12000  
personSalary[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.

Deleting items

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

package main

import (  
    "fmt"
)

func main() {  
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println("map before deletion", personSalary)
    delete(personSalary, "steve")
    fmt.Println("map after deletion", personSalary)

}

The above program deletes the key "steve" and it outputs

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

Length of the map

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

package main

import (  
    "fmt"
)

func main() {  
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println("length is", len(personSalary))

}

len(personSalary) in the above program determines the length of the map. The above program outputs, 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 internal data structure. Hence changes made in one will reflect in the other.

package main

import (  
    "fmt"
)

func main() {  
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println("Original person salary", personSalary)
    newPersonSalary := personSalary
    newPersonSalary["mike"] = 18000
    fmt.Println("Person salary changed", personSalary)

}

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

Original person salary map[steve:12000 jamie:15000 mike:9000]  
Person 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.

Maps equality

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

package main

func main() {  
    map1 := map[string]int{
        "one": 1,
        "two": 2,
    }

    map2 := map1

    if map1 == map2 {
    }
}

The above program will throw compilation 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. I would encourage you to write a program for this and make it work :).

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

That's it for maps. Thanks for reading. Have a good day.



Next tutorial - Strings