PJUllrich
Idiomatic way of handling missing Map fields
I’m extracting many fields from a map using pattern-matching against the function parameter. Sometimes only a single field is missing from the input map. I’m handling this case with Map.merge-ing a default value into the input map and calling the same function again.
However, this feels not quite idiomatic. Can you maybe think of a more idiomatic way of handling this case?
Example:
defmodule Foobar do
def foo(%{"a" => a, "b" => b, "c" => c, "d" => d, "e" => e}), do: do_something(a, b, c, d, e)
def foo(params), do: foo(Map.merge(params, %{"e" => 10}))
def do_something(a, b, c, d, e), do: IO.inspect("#{a}-#{b}-#{c}-#{d}-#{e}")
end
Foobar.foo(%{"a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5})
Foobar.foo(%{"a" => 1, "b" => 2, "c" => 3, "d" => 4})
```
Most Liked
brettbeatty
I don’t think it’s bad to use Map.merge/2, but the way you use it as a fallback seems weird (and you wouldn’t have a base case if your map didn’t have an "a" key, for example).
Here’s how I would write your module:
defmodule Foobar do
@default_params %{"e" => 10}
def foo(params) do
params = Map.merge(@default_params, params)
do_something(params["a"], params["b"], params["c"], params["d"], params["e"])
end
def do_something(a, b, c, d, e) do
IO.inspect("#{a}-#{b}-#{c}-#{d}-#{e}")
end
end
But that may just be because of how I handle options to functions. I write a lot of single-public-function modules where I’m passing around the options to a lot of private functions and want the defaulting to happen in one place. They’ll look something like this:
defmodule My.Service do
@default_opts %{charlie: true, delta: []}
def call(alpha, bravo, opts \\ []) do
opts = Enum.into(opts, @default_opts)
with :ok <- validate_something(alpha, opts),
{:ok, echo} <- do_something(alpha, bravo, opts) do
{:ok, bravo + echo}
end
end
...
end
PJUllrich
Marcus
@sigu
This case should not be the cause of using a struct. I mean the decision for a struct should be related to the problem and not to using some special syntax.
@PJUllrich
The idiomatic way for me is not to use pattern matching just for the case of extracting values from a map. The pattern matching in the parameters should have always a good reason.
For me foocould be written as:
def foo(data) do
do_something(
Map.get(data, "a"),
Map.get(data, "b"),
Map.get(data, "c"),
Map.get(data, "d"),
Map.get(data, "e", 10)
)
end
or
def foo(data) do
defaults = %{"e" => 10}
%{"a" => a, "b" => b, "c" => c, "d" => d, "e" => e} = Map.merge(defaults, data)
do_something(a, b, c, d, e)
end
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








