Maps are a handy tool that helps us store and efficiently access objects. Golang maps, in particular, behave similarly to a data structure known as a hash table.
In this topic, we'll learn the fundamental use cases of maps in Go: how to declare and initialize a map, how to add items to a map, and finally, some basic operations we can perform with items contained in a map.
What is a map?
In Go, maps are a convenient built-in data structure that associates elements of one type, known as keys, with elements of the same or another type, known as values.
Keys and values can be of any type, such as integers, floating or complex numbers, strings, pointers, etc, as long as we can compare these types using the == and != operators. An example of a type we can't use as a map key is a slice; in simple terms, we can't use slices as map keys because we can't compare one slice with another using the == operator.
An important detail to know when working with maps in Go is that all keys are unique; apart from that, we can only add items after initializing the map. Now, let's go ahead and take a look at the most basic map declaration:
var ranks map[string]string
In the above code, we have declared the map ranks with the string type key/value pairs; however, we have not initialized it yet, so its default value will be nil.
Initializing a map
As we previously mentioned, it is only possible to add items to an initialized map; if we tried to add an item to the uninitialized ranks map, a run-time panic would occur:
var ranks map[string]string
ranks["gold"] = "🥇" // Trying to add key: "gold" and value "🥇"
// Output: panic: assignment to entry in nil map
To avoid the run-time panic, we can initialize maps in various ways via the make function from Go's builtin package:
var ranks map[string]string // This only declares the 'ranks' map variable
ranks = make(map[string]string) // Option #1 to initialize the previously declared 'ranks' map
Another way is to declare and initialize the map directly:
var ranks = make(map[string]string) // Option #2 to declare & initialize the 'ranks' map
Finally, we can also assign, declare, and initialize a map using the := operator:
ranks := make(map[string]string) // Option #3 to assign, declare & initialize the 'ranks' mapAdding items to a map
Now that we have properly created and initialized our first map, let's take a look at the syntax for adding new items to a map:
ranks["gold"] = "🥇"
ranks["silver"] = "🥈"
ranks["bronze"] = "🥉"
fmt.Println(ranks) // map[bronze:🥉 gold:🥇 silver:🥈]
Since we've previously assigned the ranks map keys and values as a string, we can only pass string-type items to it.
If we know in advance the keys and values that we want our map to have, we can use a map literal to both declare and create it. A map literal starts with the map type in the form of map[KeyType]ValueType followed by curly braces {} containing the key/value pairs we want the map to start with. To add each key/value pair, first, we write the key followed by a colon :, and then the value. Commas , should separate multiple key/value pairs. Now, let's go ahead and create a new elements map:
elements := map[string]string{
"H": "Hydrogen",
"He": "Helium",
"Li": "Lithium",
}
fmt.Println(elements) // map[H:Hydrogen He:Helium Li:Lithium]Working with items on a map
Once we have added items to our map, we can work in various ways with its content. To retrieve a value from a key, we can write the following:
helium := elements["He"]
fmt.Println(helium) // Helium
If we try to retrieve a value from a non-existing key, we will retrieve the default value for the type:
sodium := elements["Na"]
fmt.Println(sodium) // Prints an empty string - the default value of the string type
It is also important to know that we can read values from an uninitialized or a nil map! If we tried to do this, we would read the default value for the type into a variable:
var numbers map[int]int
fmt.Println(numbers) // map[] <- means that the map is uninitialized
fmt.Println(numbers[2]) // 0 <- no panic output, we just get the default value: 0 for integers
fmt.Println(numbers == nil) // true <- confirms that the map is 'nil'
We can also check if a specific key exists within a map using the following syntax:
val, ok := map[key]
The above syntax will return a boolean value within the ok variable. If the key and its value are present, then the value will be returned within the val variable. Let's take a look at how we can implement it when working with the elements map:
func main() {
...
key := "He"
if val, ok := elements[key]; ok {
fmt.Println("The element", key, "is", val)
return
// Output:
// The element He is Helium
}
fmt.Println("The element", key, "is not in the map")
}
ok. It is both a common practice used by many Go programmers and a part of the Go code style conventions.Finally, we can update the value of a key in our map with the following syntax:
elements["He"] = "😈 HellBoy 😈"
fmt.Println(elements) // map[He:😈 HellBoy 😈 Li:Lithium]Conclusion
In this topic, we've learned about the map data structure in Go. Maps are made up of key/value pairs and provide a way to store data without relying on indexing. This allows us to retrieve values based on their meaning and relation to other data types.
We've also learned various ways to declare and initialize a map, how to add items to a map, and some basic operations we can do with items contained in a map. Now, let's go ahead and test our newly acquired knowledge with some tasks on maps!