Hi everybody,
this is my first proposal so I hope I get the format right, I've already
checked in the mailing list and I haven't found similar proposals.
*Problem*
When manually constructing a map using external options or the result of a
pattern match on a struct, I often find myself in the situation where I
have to add a value only if it is non-nil.
The possible approaches available today are these
*Using if*
def some_fun(s) do
MyStruct{
foo: foo,
bar: bar,
baz: baz
} = s
map = %{a: "hello", b: 42}
map =
if foo do
Map.put(map, :foo, foo)
else
map
end
map =
if bar do
Map.put(map, :bar, bar)
else
map
end
if bar do
Map.put(map, :baz, baz)
else
map
end
end
This is the obvious solution, but it gets very verbose very quickly
*Populating the map and then filtering*
def some_fun(s) do
MyStruct{
foo: foo,
bar: bar,
baz: baz
} = s
%{a: "hello", b: 42}
|> Map.put(:foo, foo)
|> Map.put(:bar, bar)
|> Map.put(:baz, baz)
|> Enum.filter(fn {_k, v} -> v end)
|> Enum.into(%{})
end
This is more concise, but it creates intermediate maps that are immediately
thrown away. Moreover, if the initial map is very large, the filter
operation is expensive since it traverses the whole map.
*Implementing a maybe_put helper function*
def some_fun(s) do
MyStruct{
foo: foo,
bar: bar,
baz: baz
} = s
%{a: "hello", b: 42}
|> maybe_put(:foo, foo)
|> maybe_put(:bar, bar)
|> maybe_put(:baz, baz)
end
defp maybe_put(map, _key, nil), do: map
defp maybe_put(map, key, value), do: Map.put(map, key, value)
This is the solution I end implementing almost always. It is present in
multiple modules across the codebase I work on (and/or put in a generic
Utils module), and talking with my colleagues I found out that is a utility
function that they also have to implement often and it has turned up at
least a couple times[1][2] also in the Elixir Forum.
[1]
https://elixirforum.com/t/elixir-way-to-conditionally-update-a-map/17952/21?u=rbino
[2]
https://elixirforum.com/t/is-there-way-to-create-map-and-conditionally-exclude-some-keys/23315/5?u=rbino
This brings me to my proposal, which is a more generic version of maybe_put.
*Proposal*
Add Map.put_if/4 to the Map module. This is more generic (and imho clearer)
than maybe_put and can be used to solve the same problem without creating
intermediate results and allowing the code to be clear and concise.
def some_fun(s) do
MyStruct{
foo: foo,
bar: bar,
baz: baz
} = s
%{a: "hello", b: 42}
|> Map.put_if(:foo, foo, foo != nil)
|> Map.put_if(:bar, bar, bar != nil)
|> Map.put_if(:baz, baz, baz != nil)
end
Let me know if you need any other information.
--
Riccardo
--
You received this message because you are subscribed to the Google Groups
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/elixir-lang-core/f513b901-fd93-4f28-88a2-f496a3c84a21o%40googlegroups.com.