To my knowledge, put_in
, Map.update
etc. all have the one limitation of not automatically creating intermediate keys when needed (for example, put_in(%{a: %{}}, [:a, :b, :c], 42)
will fail). But – despite all the risks – this would be useful to have: for instance, I often use reduce
to create a nested, aggregate representation of a list of complex objects (say, counting occurrences by categories and sub-categories), which is a one-liner when you don’t have to worry about intermediate keys (think mkdir -p
).
The implementation is simple enough (see deep_update
example below, which I’m already using all over my projects). I have a nagging feeling this must already be part of Elixir’s standard library and I just don’t know about it? If not, is there a particular reason it isn’t?
%spec deep_update(map(), [any()], any(), any() -> any()) :: map()
def deep_update(map, [leaf_key], default, update_func) do
updated_leaf = if is_map(map) and Map.has_key?(map, leaf_key) do
update_func.(map[leaf_key])
else
default
end
Map.put(map, leaf_key, updated_leaf)
end
def deep_update(map, [key | key_tail], default, update_func) do
if is_map(map) and Map.has_key?(map, key) do
new_branch = deep_update(map[key], key_tail, default, update_func)
Map.put(map, key, new_branch)
else
new_branch = deep_update(%{}, key_tail, default, update_func)
Map.put(map, key, new_branch)
end
end