In Elixir, keyword lists are a common way to pass around options and data. They’re a special type of list composed of two-element tuples where the first element is an atom (the key). Since keyword lists are just lists under the hood, you have several options for combining them, such as using the list concatenation operator ++
or using Keyword.merge/2
.
While both approaches can be used to merge keyword lists, they behave differently when it comes to handling duplicate keys. Understanding this difference is crucial when writing code that relies on expected values taking precedence.
In this post, we’ll compare ++
and Keyword.merge/2
, highlight their differences with examples, and point to relevant Elixir documentation.
Keyword lists in elixir
A keyword list looks like this:
opts = [timeout: 5000, retries: 3]
Under the hood, this is the same as:
opts = [timeout: 5000, retries: 3]
# => [{:timeout, 5000}, {:retries, 3}]
Elixir allows duplicate keys in keyword lists:
[timeout: 5000, timeout: 1000]
# => [{:timeout, 5000}, {:timeout, 1000}]
The order of keys matters, and functions in the Keyword
module are designed to handle this gracefully.
Merging with ++
The ++
operator simply concatenates two lists:
list1 = [timeout: 5000, retries: 3]
list2 = [timeout: 1000]
merged = list1 ++ list2
IO.inspect(merged)
# => [timeout: 5000, retries: 3, timeout: 1000]
Notice that both :timeout
entries are preserved. This is valid and sometimes useful, but if you access the value with Keyword.get/2
, it will return the first occurrence:
Keyword.get(merged, :timeout)
# => 5000
This behavior can lead to subtle bugs if you're expecting the second :timeout
to overwrite the first.
Merging with Keyword.merge/2
If you want later values to overwrite earlier ones, use Keyword.merge/2
:
merged = Keyword.merge(list1, list2)
IO.inspect(merged)
# => [timeout: 1000, retries: 3]
Here, the second list’s :timeout
key overwrites the first one.
You can also provide a custom function to resolve conflicts:
Keyword.merge(list1, list2, fn _key, val1, val2 -> max(val1, val2) end)
# => [timeout: 5000, retries: 3]
Summary of differences
Operation | Keeps All Duplicates? | Later Values Overwrite Earlier? | Order Preserved? |
---|---|---|---|
++ |
Yes | No | Yes |
Keyword.merge/2 |
No | Yes | Yes |
Use ++
when you want to preserve all occurrences of keys (e.g., logging, multiple options). Use Keyword.merge/2
when you want to combine options with later values taking precedence.
Learn more
By understanding the nuances of these two approaches, you can choose the right tool for merging keyword lists in Elixir and avoid common pitfalls.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.