A keyword list is a list of {key, value}
tuples. There are two way to write a keyword list:
# concise
[month: "April", year: 2018]
# explicit
[{:month, "April"}, {:year, 2018}]
Not every list of 2-tuples is a keyword list. The keys must be atoms:
Keyword.keyword?([{"month", "April"}])
# => false
If you want to use characters other than letters, numbers, and _
in the key, you need to wrap it in quotes. However, that does not make it a string - it is still an atom.
Keyword.keyword?(["day of week": "Monday"])
# => true
Keys in a keyword list can repeat, and the key-value pairs are ordered. When attempting to get a single value under a given key, you will get the first value, and any other values under the same key will be silently ignored.
list = [month: "April", month: "May"]
Keyword.get_values(list, :month)
# => ["April", "May"]
Keyword.get(list, :month)
# => "April"
Keyword lists also support the Access behaviour.
list[:month]
# => "April"
The characteristics of keyword lists made them the default mechanism for passing options to functions in Elixir.
When learning about if
, you saw a special shorter syntax:
if age >= 16, do: "beer", else: "no beer"
This may look like if
accepts two arguments, but the do:
and else:
pair is actually a single argument - a keyword list. The same code could be written as:
if age >= 16, [do: "beer", else: "no beer"]
# or
if age >= 16, [{:do, "beer"}, {:else, "no beer"}]
The usage of keyword lists as function options is so common that Elixir allows you to skip the square brackets when the keyword list is the last argument passed to a function.
Since tuples, lists, maps, and others are treated the same as function calls in Elixir syntax, this property is also available to them.
[1, 2, three: 3]
# => [1, 2, {:three, 3}]
To successfully pattern match on a keyword list using the concise syntax, you would need to specify all of the keys in the correct order. This makes pattern matching an unlikely choice for working with keyword lists. Use the Keyword
module or Access behaviour instead.
[month: month] = [month: "April", year: 2018]
# => ** (MatchError) no match of right hand side value: [month: "April", year: 2018]
[year: year, month: month] = [month: "April", year: 2018]
# => ** (MatchError) no match of right hand side value: [month: "April", year: 2018]
Keyword list | Map | |
---|---|---|
Key type | Atoms | Any, can be mixed in one map |
Duplicate keys | Yes | No |
Keys ordered | Yes | No |
Access |
list[:key] , Keyword.get/2
|
map.key , map[:key] , Map.get/2
|
Access time | Linear | Logarithmic |
Pattern matching | Not very useful | Useful |
Faster access time, flexible key type, and useful pattern matching makes maps the default choice in most cases.
Use keyword lists when you don't have a lot of data, but need duplicate keys or keys in a specific order.