Maps
Apart from keyword lists, Elixir allows storing key-value pairs in maps. A map is created using the %{} syntax.
Compared to keyword lists, maps:
- allow any value as a key
- don’t allow duplicate keys
- aren’t ordered - you shouldn’t rely on their internal ordering
- are faster to operate on, especially with big number of keys
Create an empty map:
%{}
Maps can have keys and values of different types:
%{1 => "a", :b => 10}
In maps, order doesn’t matter:
%{1 => "a", :b => 10} == %{:b => 10, 1 => "a"}
Now let’s explore the operations available for working with maps.
Map module operations
The Map module provides a very similar API to the Keyword module with convenience functions to add, remove, and update maps keys:
Get a value from a map:
map = %{:a => 1, 2 => :b}
# 💡 Try changing Map.get to Map.delete
Map.get(map, :a)
You can also use bracket syntax:
# 💡 Try changing :a to :x
map[:a]
Add a new key-value pair:
# 💡 Try changing "c" to 2, which is already in the map. What happens?
Map.put(%{:a => 1, 2 => :b}, "c", 3)
Convert a map to a list of tuples:
# 💡 Let's change the map, so that Map.to_list returns a keyword list
Map.to_list(%{:a => 1, 2 => :b})
Maps with predefined keys
It’s common to create maps with a predefined set of keys. Their values may be updated, but new keys are never added nor removed. This is useful when we know the shape of the data we are working with.
In such cases, the keys are most often atoms. Elixir provides syntax sugar for this: %{key1: value1, key2: value2}.
A map representing a user:
%{:name => "John", :age => 23}
The same user using syntax sugar:
%{name: "John", age: 23}
Dot notation for atom keys
When a map has atom keys, you can use convenient dot notation for accessing (map.key) and updating (%{map | key: value}).
This syntax is best when working with predefined atom keys, because it raises an error when the key doesn’t exist. This gives you feedback early on if you accidentally use a wrong key. This syntax also powers another Elixir feature called “Structs”, which we will learn later on.
Access a field:
user = %{name: "John", age: 23}
# 💡 What happens if you try to get user's surname, which is not there?
user.name
Update a field:
# 💡 Try replacing age with a non-existent key
%{user | age: 24}
Summary
In this chapter we learned about keyword lists and maps. To sum up:
- Use keyword lists for passing optional values to functions
- Use maps for general key-value data structures
- Use maps when working with data that has a predefined set of keys