I’m trying to use Kernel.put_in to insert data on nested maps. This works fine, unless I want to put data on a nil-value.
Example:
m = %{}
Kernel.put_int(m, [:foo, :bar], “baz”)
raises:
** (ArgumentError) could not put/update key :bar on a nil value
(elixir) lib/access.ex:309: Access.get_and_update/3
(elixir) lib/map.ex:694: Map.get_and_update/3
(elixir) lib/kernel.ex:1831: Kernel.put_in/3
since there is no element with the key :foo in the map. How would you implement a function that will create an empty map with the key :foo if it does not exist yet?
Expected result:
m = %{}
my_put_in(m, [:foo, :bar], “baz”)
%{foo: %{bar: “baz”}}
If you plan on working on a map with a regular structure if might be a good idea to use a struct. This would give you a default structure that you can work on. If you do you’ll have to change your call to be
unfortunately both structs and pattern matching are probably not viable since I’m working on data coming in from json objects, where the structure is not (or only loosely) known before.
I will dig into the Access behaviour and have a look if I can find some clues there.
I was able to figure it out by having a look at Kernel.put_in and Access.get_and_update. The key was to override the Access.get_and_update(nil, key, fun) function (which basically meant, re-implementing the whole Kernel- and Access.Access.get_and_update functions, unfortunately. I’m also not sure if there are still bugs there, but for other people wondering, here is the gist: https://gist.github.com/ifoo/334d11f4b7cb5491447d39539ebf85bb