Elixir Course Online | Prograils - Software Development Company
Maps
Representation
Maps are a key-value data structure where key and value can be represented by any type:
%{ :first_name => "User", :last_name => "Example", :age => 30 }
If the key is an atom, you can use the same shortcut that you use with keyword list:
%{ first_name: "User", last_name: "Example", age: 30 }
The use of a map is similar to the keyword list since it also represents values by keys. However, there are some differences:
You can not duplicate keys, because if you do, only the last key-value occurrence will be used:
iex> %{ x: 10, x: 15 } %{x: 15}
Elements of map are not ordered, which is also different to keyword list:
iex> %{last_name: 'Example', first_name: 'User'} %{first_name: 'User', last_name: 'Example'}
Inserting new elements to a map might be slower than in case of keyword list since the second one (in best case scenario) is just prepending a new element to the beginning of a list.
Accessing elements is much faster when using map (constant time access), since keyword list need to iterate over its elements.
A map is used to store actual payload data since they have a hash-based implementation.
Accessing map elements
You can access map elements with square-bracket syntax:
iex> map = %{ :first => 0, "second" => 1, { :third } => 3 }
%{:first => 0, {:third} => 3, "second" => 1}
iex> map[:first]
0
iex> map["second"]
1
iex> map[{ :third }]
3
iex> map[:non_existing_key]
nil
There is a handy shortcut for atom-type keys:
iex> map.first
0
When there is no match you get a KeyError
:
iex> map.non_existing_key
** (KeyError) key :non_existing_key not found in: %{:first => 0, {:third} => 3, "second" => 1}
Notice the difference between .
and []
syntax when accessing a non-existing element:
iex(5)> map[:non_existing_key]
nil
iex(6)> map.non_existing_key
** (KeyError) key :non_existing_key not found in: %{:first => 0, {:third} => 3, "second" => 1}
Updating map elements
Elixir has a very handy syntax for updating values of a given keys in map:
iex> person = %{ first_name: "User", last_name: "Example" }
%{first_name: "User", last_name: "Example"}
iex> person = %{ person | first_name: "Michael", last_name: "Johnson" }
%{first_name: "Michael", last_name: "Johnson"}
iex> person = %{ person | age: 23 }
** (KeyError) key :age not found in: %{first_name: "User", last_name: "Johnson"}
(stdlib) :maps.update(:age, 23, %{first_name: "User", last_name: "Johnson"})
(stdlib) erl_eval.erl:259: anonymous fn/2 in :erl_eval.expr/5
(stdlib) lists.erl:1263: :lists.foldl/3
As you can see you can use the following syntax to update a value of key:
map_variable = %{ map_variable | key-value pairs }
There are also two things worth to mention:
you can specify multiple key-value pairs,
you can't modify a value of non-existing key.
Nested maps
Maps can be nested:
iex> person = %{ first_name: "User", last_name: "Example", address: %{ city: "New York", country: "USA", street: "Wall St" } }
%{
address: %{city: "New York", country: "USA", street: "Wall St"},
first_name: "User",
last_name: "Example"
}
You can easily access nested data with []
or .
(when dealing with atoms) operator:
iex> person[:address][:city]
"New York"
iex> person.address.city
"New York"
Update is not so obvious, you can't just simply assign new value this way:
iex> person.address.city = "Washington"
** (CompileError) iex:4: cannot invoke remote function person.address/0 inside match
Instead, you should use following syntax:
iex> person = put_in person[:address][:city], "Washington"
%{
address: %{city: "Washington", country: "USA", street: "Wall St"},
first_name: "User",
last_name: "Example"
}
It not only works for updating data, but also for putting new ones:
iex> person = put_in person[:address][:number], 1
%{
address: %{city: "Washington", country: "USA", number: 1, street: "Wall St"},
first_name: "User",
last_name: "Example"
}
You can delete given key with pop_in/2
function:
iex(4)> {_, person} = pop_in(person, [:address, :street])
{"Wall St",
%{
address: %{city: "New York", country: "USA"},
first_name: "User",
last_name: "Example"
}}
iex(5)> person
%{
address: %{city: "New York", country: "USA"},
first_name: "User",
last_name: "Example"
}