@script I’m a bit lost or don’t remember something. Should not it be:
%{
"antibiotic" => %{"$binding" => :last},
"prescribing_clinician" => %{"$binding" => :last}
}
If I remember it correct $binding
set to :last
should be a leaf and should not have $include
inside it. If so then your input is invalid, right?
Also I made one small mistake with return (noticed when you gave me this line after changes):
defmodule Example do
def sample(map) when is_map(map), do: do_sample(map)
defp do_sample(map), do: Enum.reduce(map, [], &do_sample/2)
defp do_sample({key, %{"$include" => include}}, acc) when is_map(include),
do: [{String.to_atom(key), do_sample(include)} | acc]
defp do_sample({key, %{"$include" => include}}, acc) when is_bitstring(include),
do: [{String.to_atom(key), String.to_atom(include)} | acc]
defp do_sample({key, %{"$include" => include}}, acc) when is_list(include),
do: [{String.to_atom(key), Enum.map(include, &String.to_atom/1)} | acc]
# before I did not used `acc` here:
defp do_sample({key, %{"$binding" => :last}}, acc), do: [String.to_atom(key) | acc]
defp do_sample({_key, _value}, acc), do: acc
end
If we would call it with such input:
data = %{
"antibiotic" => %{"$binding" => :last},
"prescribing_clinician" => %{"$binding" => :last}
}
data |> Example.sample() |> IO.inspect()
then it would return:
[:prescribing_clinician, :antibiotic]
Again the better way is to create a test for all cases. Once you do it you should have all information you need.
Also looks like that you do not understand my code, so I would describe it:
defmodule Example do
# Enum.reduce/3 would be used multiple times
# this is function to work with only top level
# so reduce which happens also nested is called in separate function
# in order to allow easy change of code
def sample(map) when is_map(map), do: do_sample(map)
# this is used in `sample/1` and next clausule
defp do_sample(map), do: Enum.reduce(map, [], &do_sample/2)
# checks if our value have map with `$include` as key
# then we also check if `include` is map which is important for rest clausules
defp do_sample({key, %{"$include" => include}}, acc) when is_map(include),
do: [{String.to_atom(key), do_sample(include)} | acc]
# checks if our value have map with `$include` as key
# then we also check if `include` is string which is important for rest clausules
defp do_sample({key, %{"$include" => include}}, acc) when is_bitstring(include),
do: [{String.to_atom(key), String.to_atom(include)} | acc]
# checks if our value have map with `$include` as key
# then we also check if `include` is list
# as you did not gave a example with list of maps
# I made it this clausule working with strings only
defp do_sample({key, %{"$include" => include}}, acc) when is_list(include),
do: [{String.to_atom(key), Enum.map(include, &String.to_atom/1)} | acc]
# as described before if we have leaf then we do not go nested
# this is only a small optimization in order to call another iterate again
defp do_sample({key, %{"$binding" => :last}}, acc), do: [String.to_atom(key) | acc]
# if nothing else matches then simply return acc
# you can debug here which things does not match
defp do_sample({_key, _value}, acc), do: acc
end
Also don’t worry in case of any questions.