23. Maps

Let's navigate!

Map Type

Map is collection of key value pairs. It goes by various names in other programming languages like dictionaries in Python, objects in JavaScript etc.

Maps are represented as map[K]V, where K and V are the types of its keys and values.

Declaring Maps

We can declare map using var declaration. We are defining string to be keys for the map and string to be values. Since map is declared but not initialized with any value, it will be a nil map.

main.go
var capitals map[string]string
fmt.Println(capitals)
fmt.Println("is map nil ==>", capitals == nil)

Creating map

Maps are created using make function call. make will perform the required memory allocation required to use map. We can add elements to map using map[key] = value semantics.

main.go
caps := make(map[string]string)
// we can also use var keyword as below
// var caps map[string]string = make(map[string]string)
fmt.Println(caps)
fmt.Println("is map nil ==>", caps == nil)
// returns false
caps["India"] = "Delhi"
caps["England"] = "London"
caps["U.S."] = "Washington"
fmt.Println(caps)

Map Literals

We can also use map literal to declare and initialize maps. make function call is not required when using map literal.

main.go
capitalsOfCountries := map[string]string{
"India": "Delhi",
"England": "London",
"U.S.": "Washington",
}
fmt.Println(capitalsOfCountries)

Accessing map elements

map elements can be accessed using subscript notion similar to array and slices. In array and slices, index can only be integers. For maps, however, key can be anything which can be compared using == comparison.

main.go
//accessing map elements
fmt.Println(capitalsOfCountries["India"])
fmt.Println(capitalsOfCountries["England"])

If we try to access map key which does not exists, it will not cause an error, but will return zero value.

main.go
fmt.Println(capitalsOfCountries["XYZ"])
// prints empty string

Zero values for map

Zero value for map is nil. Zero value for map element is zero value for type being stored as value.

main.go
var s map[string]string
var i map[string]int
fmt.Println("is s nil =>", s == nil)
fmt.Println("is i nil =>", i == nil)

If map is not nil, but empty, accessing its elements will returns its zero value.

main.go
// zero value for map elements
j := map[string]int{}
fmt.Println(j["India"]) // output : 0

Notice that, event if key does to exist in empty map, it did return zero value.

Nil and empty map

Similar to slices, maps can be empty or nil. When we use make function call or use map literal for declaring map, it initializes empty map

main.go
var t = make(map[string]string)
k := map[string]int{}
fmt.Println("is t nil =>", t== nil)
fmt.Println("is k nil =>", k== nil)

Both nil and empty maps have len() as zero.

main.go
var s map[string]string
var t = make(map[string]string)
fmt.Println(len(s))
fmt.Println(len(t))

Iterating over map

Iterating over maps is similar to array and slices, we can use for...range loop for iteration. range works as follows for a map

for key,value :=range map { // }

main.go
for key, value := range caps {
fmt.Printf("key =>%v, value => %v\n", key, value)
}

Unlike arrays and slices, maps do not guarantee same order. Map is unordered collection of key-value pairs.

If either key or value is not required, we can omit it by using blank identifier.

main.go
for _, value := range caps {
fmt.Printf("value => %v\n", value)
}
for key, _ := range caps {
fmt.Printf("key =>%v\n", key)
}

Finding element in map

To find element in map, we provide key to the map to get element/value returned from map. A map does not panic if we try to access key which is not present, instead it returns zero value. To identify if value is returned is actual value present on map or zero value returned due to absence of key, map returns a second value, ok which tells if key was found or not.

If we see output for below code snippet, when we check for XYZ and ABC, both returns empty string. For XYZ, value is empty string, while for ABC, it is missing from map.

main.go
cocs := map[string]string{
"India": "Delhi",
"England": "London",
"U.S.": "Washington",
"XYZ": "",
}
fmt.Printf("Checking captial for India-> %v\n",
cocs["India"])
fmt.Printf("Checking captial for XYZ-> %v\n",
cocs["XYZ"])
fmt.Printf("Checking captial for ABC-> %v\n",
cocs["ABC"])

To distinguish between empty/zero value and missing value, we can use value,ok semantics.

main.go
value, ok := cocs["XYZ"]
if ok {
fmt.Printf("Value is %v \n", value)
} else {
fmt.Println("Key not found")
}
value, ok = cocs["ABC"]
if ok {
fmt.Printf("Value is %v \n", value)
} else {
fmt.Println("Key not found")
}

Updating element in map

Updating value is similar to creating a value. We can use

map[key]= value semantic to update value for given key.

main.go
capitals = map[string]string{
"India": "",
"England": "London",
"U.S.": "Washington",
}
capitals["India"] = "Delhi"
fmt.Println(capitals)

Deleting element from map

Deleting value from map is straightforward. Go provides inbuilt delete function to delete a value from map. Semantics for delete is

delete(mapName,key)

main.go
capitals = map[string]string{
"India": "Pune",
"England": "London",
"U.S.": "Washington",
}
delete(capitals, "England")
fmt.Println(capitals)

Comparing maps

Similar to slices, maps can not be compared directly. We can only check if a map is nil or not directly. For other comparisons, we need to write our own for loop for comparison.

GitHub Code

Blog Posts

Videos

GopherCon 2016: Keith Randall - Inside the Map Implementation