Map in go is similar to what object is in JavaScript, hash in Python. I will be heavily using JavaScript as reference to discuss maps in go programming language, because that’s the one of the languages I excel in.
In go programming language a map looks like this map[keyType]valueType
. So how do you declare and use a map in Go?
Simple
var names map[string]string
Here we have declared a names map whose keys are string and the values are also string. Now that we have declared a map, how do we use it?
If we try to use it like
names["ceo"] = "Zuck"
We will get a panic: assignment to entry in nil map
error. Let me explain the reason behind the error. Maps are reference types like slices or pointers, when we declare a map like this, it doesn’t point to anything, it’s a nil map, you can read from a nil map, it will act like an empty map, but trying to write to it will result in an error.
Hmm, confusing, isn’t it? So how do we declare and use a map in Go? We use the make
function again to create maps in Go.
names := make(map[string]string)
names["ceo"] = "Zuck"
names["cto"] = "Andrew"
You can see it in action here : https://go.dev/play/p/cjTO5klGADe
Why this works, is the make function initializes a hash map data structure and returns a value which points to it. I personally find it a bit odd as in JavaScript you can just declare an object like this
var names = {
ceo: "Zuck",
cto: "Andrew"
}
We can also do something similar in Go as well,
names := map[string]string{
"ceo": "Zuck",
"cto": "Andrew"
}
I like this compared to using make function, because it feels more natural, but it’s a personal choice, and make has some tricks up its sleeve, which is not possible without it.
With this syntax we can also create an empty map (not a nil map)
names := map[string]string{}
Neat, isn’t it? That’s the gist of how we can create maps with or without values in Go. Just remember don’t try to write to a nil map, or you will have a bad time!
Now let’s see how we interact with a map in Go.
Iterating over: We can iterate a map using the range keyword
for k, v := range names {
fmt.Println(k, v)
}
Working example: https://go.dev/play/p/qPUj26njUk3
Number of Items: We can use the built in len
function to check the available items on a map.
Working example: https://go.dev/play/p/pWNpyMhbSqF
Deleting an Item: We can use the delete function to delete an item from a map. It returns nothing on success or on failure. The delete
function takes two arguments, the first is the map and second is the key.
delete(names, "ceo") // will delete zuck from facebook :-)
Working example: https://go.dev/play/p/qhjlxOfFtgV
Accessing Items: Accessing an item is pretty straight forward, names["ceo"]
will give you the item for key “ceo”, here is the interesting bit, this also returns a second value, a boolean, it indicates if something exist on the map or not.
_, ok := names["ceo"]
if ok {
fmt.Println("Zuck lives on")
}
Working example: https://go.dev/play/p/RCReolabhYq
Key Types: According to official Go documentation, all primitive data types which can be compared using ==
can be used as a key in maps.
Which means you can use struct as a key for a map. Trippy, isn’t it?
Here is a snippet of a map of maps, where the key is a string and the value is another map with string keys and int values.
pokemonxp := make(map[string]map[string]int)
// First, initialize the inner map for "red_blue" generation
pokemonxp["red_blue"] = make(map[string]int)
// Now we can add charizard's XP
pokemonxp["red_blue"]["charizard"] = 1000
// Access the value
charizardxp := pokemonxp["red_blue"]["charizard"]
fmt.Println(charizardxp) // Output: 1000
Adding item to this type of map is a bit difficult to maintain, as before I can add charizard
to the map
I have to make sure red_blue
generation exists within the map first (Charizard was introduced first in Pokemon Red and Blue). So instead if we used a Struct as key, it would be easier to maintain.
type Key struct {
Generation, Name string
}
pokemonxp := make(map[Key]int)
pokemonxp[Key{"red_blue", "Charizard"}] = 1000
pokemonxp[Key{"red_blue", "Raichu"}] = 800
fmt.Println(pokemonxp[Key{"red_blue", "Raichu"}]) // Output: 800
Live example: https://go.dev/play/p/X1r_RXFD6jS
The three types that can’t be used as map keys are: slices, maps, and functions
Accessing Zero Values: Trying to access from a nil map, as you already know would result in returning zero value. Defaults to the type of values intended to be kept in the map.
Working example: https://go.dev/play/p/0FRbPXTkCtq
It outputs an empty string("") that’s why we don’t see anything as output. If the value type was number, zero would have been returned.
There are a few caveats while using Maps in go, for example Map is by default pass by reference, so if you pass a Map to a function and make change to it, it will also mutate the original value.
Example:
package main
import "fmt"
func addToMeta(names map[string]string, position string, name string) map[string]string {
names[position] = name
return names
}
func main() {
names := make(map[string]string)
names["ceo"] = "Zuck"
names["cto"] = "Andrew"
newNames := addToMeta(names, "ceo", "elon")
fmt.Println(newNames["ceo"]) // elon
fmt.Println(names["ceo"]) // elon
}
Working example: https://go.dev/play/p/wMUaaTZ0QuL
Another caveat is Map doesn’t bode well with concurrency, I am planning to write a whole article on it, in short it’s not possible to read and write to maps using goroutines, it causes a panic. We have to use RWMutex
to make it work.
You can read about it here: https://go.dev/blog/maps
Conclusion: Maps in Go are powerful and flexible, it’s absolutely essential to understand how to use them effectively. You will find yourself using maps in almost every Go program you write. I hope this article has helped you understand the basics of maps in Go programming language.
Feel free to file an issue or suggest an edit if you find something wrong or missing in this article.