def create(conn, %{"a" => a_params}) do
with true <- someClause(),
a_params <- someClause(a_params),
:ok <- Enum.reduce_while(get_in(a_params, ["b"]), %{}, fn {_key, %{"c" => c , "d" => d}}, _iteration ->
case Module.check(c, d) do
:ok -> {:cont, :ok}
{:error, "..."} ->
{:halt, {:error, "..."}}
end
end), do
#do things
else
{:error, "..."} -> throw errors
end
end
In some tests, %{b} in a_params would be nil
So reduce_while/3 would fail
How can I prevent reduce_while/3 from executing if my map is nil along the above condition ?.
Thank you.
not sure if I’m misunderstanding, but if you only have one key to access just use Map.get(map, key, default).
Or what do you want to happen if it’s nil?
Hi @KristerV
Thanks for your reply, I need to access multiple keys and that would return one variable only, and if it’s nil, just return :ok, let it pass, because some scenarios that are not involved in that map
I would factor it like this which I think is a bit clearer and fits your use case?
def create(conn, %{"a" => a_params}) do
with true <- some_clause(),
a_params <- some_other_clause(a_params),
:ok <- check(a_params) do
IO.puts("Do some stuff")
end
end
def check(%{"b" => b}) do
Enum.reduce_while(b, %{}, fn
{_key, %{"c" => c , "d" => d}}, _iteration ->
check_module(c, d)
_other, _iteration ->
{:halt, {:error, "..."}}
end
end
def check(other) do
{:error, "..."}
end
def check_module(c, d) do
case Module.check(c, d) do
:ok ->
{:cont, :ok}
{:error, "..."} ->
{:halt, {:error, "..."}}
end
end
def check(other) do
{:error, "..."}
end
I typically find that a function over a few lines long is hard to read and harder to refactor (and this refactor definitely isn’t all it could be). Multiple function clauses (both named and anonymous) are your friends here.
you know i re-read your question and realized you can just add a filter into the with statement above the reduce.
iex(1)> a = %{"b" => 1}
%{"b" => 1}
iex(2)> with val when not is_nil(val) <- Map.get(a, "b"), do: true
true
iex(3)> a = %{}
%{}
iex(4)> with val when not is_nil(val) <- Map.get(a, "b"), do: true
nil
Or personally I would look for a way where there’s less if statements like that. Instead the result of the reduce should always be the same datatype. In case of the map, an empty map. I think replacing get_in(a_params, ["b"]) with Map.get(a_params, "b", []) would accomplish that.