Joy of Elixir

4. Marvellous maps

Back in the last chapter, Roberto tried tracking information about the people assembled using some lists within another list:

iex> those_who_are_assembled = [
...> ["Roberto", "30ish", "Male"],
  ... a long time passes ...
...> ["Young Roberto", "Male", "20ish"],
...> ]

But as we can see here, the order of the items within the list can be input incorrectly due to human error. We need something that helps prevent human error like this, and if that something could helpfully indicate what each item in the list was -- by giving it a kind of a name -- then that would be good too.

For that, we can use a map. Just like a regular map, maps in Elixir can tell us where to find things, if only we knew where to look.

Let's look at how we could collect a single person's data using a map:

iex> person = %{"name" => "Roberto", "age" => "30ish", "gender" => "Male"}
%{"age" => "30ish", "gender" => "Male", "name" => "Roberto"}

In a map, we structure data using keys and values. The thing to the left of the => is called a key and the thing to the right is called a value.

Just like on a map of something like a mall, a key here tells us where to find a particular value. If, for instance, we wanted to know the age of this person, we can use the key "age" to pluck out that value. We'll see that in a moment.

Unlike strings and lists where we can use a single character at the start and end to show Elixir where the starts and ends are -- double quotes for strings, square brackets for lists -- we need to use %{ to tell Elixir where a map starts and } to show where it ends.

You might notice here that the computer has taken in our map and returned the keys in a different order to the one that we specified. The order of keys doesn't matter in a map at all, unlike in lists where order does matter. It would be pretty strange to spend all the time ordering the list only for Elixir to return it in a non-sensical order. For instance, if we had a list like this:

favourite_people = ["The Reader", "Roberto", ...]

This list indicates that "The Reader" is the 1st favourite person, and that Roberto is a (close) second. The order here matters. For maps, it doesn't matter what order the keys and their corresponding values are in because the map would still be the same:

iex> person = %{"gender" => "Male", "age" => "30ish", "name" => "Roberto"}
%{"age" => "30ish", "gender" => "Male", "name" => "Roberto"}

The position of the "name" key within a map has no meaning. The keys for a map can be in any order.

You may now be thinking about how to get the data back out of a map once you've put it in. We talked about how to "pluck" that value out before, but didn't see an example yet. In order to access a value from a map we need to know the corresponding key. Once we know the corresponding key, then we can use [] to pluck that value out of the map. Think of it like the claw from a skilltester, diving in to pick out the value.

skilltester

Unlike a skilltester, these square brackets aren't rigged to drop the value mere inches from the chute; these square brackets have an iron grip. For instance, if we wanted to get out the value of "name" for person we can do:

iex> person["name"]
"Roberto"

And if we want to get the age, we would use the "age" key:

iex> person["age"]
"30ish"

Roberto lets out a thoughtful "Mmmmmmmmmm" in something very close to an agreement. He likes maps. Now Roberto can know the exact data that we're collecting about the assembled masses, without a concern for how the data ordered, and then use that for later on. We don't know yet what Roberto has in mind for the data, but he's collecting it for a reason. Or at least, it seems that way.

To collect data about all the people assembled here before us, we can create a list of maps:

iex> those_who_are_assembled = [
...> %{"age" => "30ish", "gender" => "Male", "name" => "Roberto"},
...> %{"age" => "30ish", "gender" => "Male", "name" => "The Author"},
...> %{"age" => "Unknowable", "gender" => "Unknowable", "name" => "The Reader"},
...> %{"age" => "38", "gender" => "Female", "name" => "Juliet"},
...> %{"age" => "21", "gender" => "Female", "name" => "Mary"},
...> %{"age" => "67", "gender" => "Female", "name" => "Bobalina"},
...> %{"age" => "54", "gender" => "Male", "name" => "Charlie"},
...> %{"age" => "10", "gender" => "Male", "name" => "Charlie (no relation)"},
...> ]

Roberto excitedly disappears into the crowd again with this new knowledge.