Suppose I have a map in Elixir:
m = %{"a"=>1, "b"=>2, "c" => 3}
If I call Map.values(m)
, am I guaranteed that the return value will always be [1, 2, 3]
in that order and not say, [3, 1, 2]
?
This is one thing I am not clear on from the docs. After some preliminary testing, I think it is.
Maps' keys do not follow any ordering.
12.3) Maps are the "go to" key-value data structure in Elixir. Key-value pairs in a map do not follow any order (that's why the printed map in the example above has a different order than the map that was created).
Maps are the "go to" key-value data structure in Elixir. Maps can be created with the % {} syntax, and key-value pairs can be expressed as key => value: Key-value pairs in a map do not follow any order (that's why the printed map in the example above has a different order than the map that was created).
Note that map.key will raise a KeyError if the map doesn't contain the key :key, compared to map [:key], that would return nil. Note: do not add parens when accessing fields, such as in data.key () . If parenthesis are used, Elixir will expect data to be an atom representing a module and attempt to call the function key/0 in it.
Key-value pairs in a map do not follow any order (that's why the printed map in the example above has a different order than the map that was created). Maps do not impose any restriction on the key type: anything can be a key in a map. As a key-value structure, maps do not allow duplicated keys.
Keyword list is a list of tuples, where the first tuple element is an atom. In Elixir it is used as an optional list of function parameters. Many Elixir libraries stick to that convention. We have two options to define a Keyword list and two options to use it as a function parameter.
The implementation of Maps in Elixir and Erlang has some confusing properties. For small values of entries it is a sorted key list, and thus appears to have the properties you see in simple experiments.
Above a certain number of entries (32 I think), the implementation switches to Hash Array Mapped Trie and all the properties you see disappear. You can not depend on the order of either the keys or the values of a map in the general case. See
https://en.wikipedia.org/wiki/Hash_array_mapped_trie
for an explaination of the underlying structure of Map.
iex(7)> 1..33 |> Enum.reduce(%{}, fn(x, acc) -> Map.put(acc,x,x) end ) %{11 => 11, 26 => 26, 15 => 15, 20 => 20, 17 => 17, 25 => 25, 13 => 13, 8 => 8, 7 => 7, 1 => 1, 32 => 32, 3 => 3, 6 => 6, 2 => 2, 33 => 33, 10 => 10, 9 => 9, 19 => 19, 14 => 14, 5 => 5, 18 => 18, 31 => 31, 22 => 22, 29 => 29, 21 => 21, 27 => 27, 24 => 24, 30 => 30, 23 => 23, 28 => 28, 16 => 16, 4 => 4, 12 => 12} iex(8)> Map.keys(v(7)) [11, 26, 15, 20, 17, 25, 13, 8, 7, 1, 32, 3, 6, 2, 33, 10, 9, 19, 14, 5, 18, 31, 22, 29, 21, 27, 24, 30, 23, 28, 16, 4, 12] iex(9)> Map.values(v(7)) [11, 26, 15, 20, 17, 25, 13, 8, 7, 1, 32, 3, 6, 2, 33, 10, 9, 19, 14, 5, 18, 31, 22, 29, 21, 27, 24, 30, 23, 28, 16, 4, 12]
From the Elixir website:
Compared to keyword lists, we can already see two differences:
- Maps allow any value as a key.
- Maps’ keys do not follow any ordering.
While the Elixir website clearly states that Maps do not follow any ordering, they do follow a specific order after they've created (but do not preserve their order of creation). It seems that the Maps are organized alphabetically according to their keys (but I have nothing to back this up except a few experiments in IEx):
map = %{c: 3, a: 1, b: 2} map # => %{a: 1, b: 2, c: 3} Map.keys(map) # => [:a, :b, :c] Map.values(map) # => [1, 2, 3]
Since you asked about preserving the original order, The answer is NO.
A better alternative would be to use Keyword lists (which are a linked-list of two element tuples underneath). Because of this, the order of their creation is maintained:
kw = [c: 3, a: 1, b: 2] kw # => [c: 3, a: 1, b: 2] Keyword.keys(kw) # => [:c, :a, :b] Keyword.values(kw) # => [3, 1, 2]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With