Advanced composite types: Maps
This chapter will be lighter than the :doc:`previous one<slices>`, so don't worry, and smile for the perspective of adding a new tool to your box.
Arrays and slices are wonderful tools for collecting elements of the same type
in a sequential fashion.
We simply say: the first element, the second element, ... the n th
element of the array or the slice. We can access and modifiy elements using
their numerical integer indices in the
array or the slice.
This is nice and quite useful. Now suppose that we'd like to access and modify elements given a name of some type (a non-integer name or 'index'). For example: Say we wish to access the definition of the word "Hello" in a dictionary or to find out the capital of "Japan".
These category of problems call for a new kind of type. A type where we can specify a key from which a value will be stored and retrieved. It's not about the nth element of a sequence, it's about an association of things (keys) with other things (values).
This kind of types is called a dict in Python and map in Go and a hash in Ruby.
How to declare a map?
The syntax of a map type is:
You can access and assign a value to an entry of this map using the square
bracket syntax as in arrays or slices, but instead of an
int index you use a
key of type
As with slices, since maps are reference types, we can make them using the
When used with maps,
make takes an optional capacity parameter.
We now have the idea: it's like a table with two columns: in the left column we have the key, and on the right column we have its associated value.
Some things to notice:
- There is no defined notion of order. We do not access a value by an index, but rather by a key instead.
- The size of a map is not fixed like in arrays. In fact, just like a slice, a map is a reference type.
- Doing for example
numbers["coffees_I_had"] = 7will actually add an entry to this table, and the size of the map will be incremented by 1.
- As in slices and arrays, the built-in function
lenwill return the number of keys in a map (thus the number of entries).
- Values can be changed, of course.
numbers["coffees_I_had"] = 12will change the
intvalue associated with the string "coffes_I_had".
Literal values of maps can be expressed using a list of colon-separated
key:value pairs. Let's see an example of this:
Maps are references
If you assign a map
m to another map
m1, they will both refer to the
same underlying structure that holds the key/value pairs.
Thus, changing the value associated with a given key in
m1 will also change
the value of that key in
m as they both reference the same underlying data:
Checking existence of a key
Question What would the expression
rating["C#"] return as a value, in our previous
Good question! The answer is simple: the expression will return the zero value of the value type.
The value type in our example is
float32, so it will return '0.00'.
But then, if the value associated with an inexistent key is the zero of the type value, how can we be sure that C#'s rating is actually 0.00? In other words: is C#'s rating actually 0.00 so we can say that as a language it stinks or was it that it was not rated at all?
Here comes the "comma-ok" form of accessing a key's associated value in a map.
It has this syntax:
value, present = m[key].
present is a boolean that indicates whether the key is present in the
We often use
ok as a variable name for boolean presence, hence the name
"comma-ok" form. But, hey! You're free to use any name as long as it's a
Deleting an entry
To delete an entry from the map, think of an inversed "comma-ok" form.
In fact, you just have to assign any given value followed by comma
If in line 2, we had
map["C++"] = 1, true the output of the if-else
statement would be: C++ is in the map and its rating is 1. i.e. the entry
associated with key "C++" would be kept in the map, and its value changed to 1.
Cool! We now have a sexy new type which allows us to easily add key/value pairs, check if a given key is present, and delete any given key. Simply.
The question now is: "How do I retrieve all the elements in my map?"
More specifically, "How do I print a list of all the languages in the
map with their respective ratings?"
The range clause
For maps (and arrays, and slices, and other stuff which we'll see later), Go comes with a facinating alteration of the syntax for the "for statement".
This syntax is as follow:
Let's see a complete example to understand this better.
If we don't need the value in our for statement, we can omit it like this:
Exercise: Modify the program above to replace the last comma in the list by a period. That is, output: "We rated these languages: C++,C,Go,Python."
This "for statement" form is also available for arrays and slices where instead of a key we have an index.
Let's rewrite a previous example using this new tool:
Look carefully at line 6. We used a
range over a slice of ints.
i is an
index and it goes from 0 to
value is an int that goes from
Notice also how we didn't use the index
i in this loop. We didn't need it.
The blank identifier
Whenever a function returns a value you don't care about, or a range returns an index that you don't care about, you can use the blank identifier _ (underscore) This predeclared identifier can be assigned any value of any type, and it will be discarded.
We could have written the
Max(s int)int function like this:
Also, say, we have a function that returns two or more values of which some are unimportant for us. We can "ignore" these output results using the blank identifier.
That's it for this chapter. We learned about maps ; how to make them, how to
add, change, and delete key/value pairs from them. How to check if a given key
exists in a given map. And we also learned about the blank identifier '
range clause, especially, is a control flow construct that should
belong in the chapter about :doc:`control flow<control>`, but we didn't yet
know about arrays, slices or maps, at that time. This is why we deferred it
until this chapter.
The next chapter will be about things that we didn't mention about functions in the chapter about :doc:`functions <functions>` for the same reason: lack of prior exposure, or simply because I wanted the chapter to be light so we can make progress with advanced data structures.
Anyways, you'll see, it will be fun! See you in the next chapter! :)