I apologize for not being able to describe my question clearly in the title.
I’m not sure how to name this coding pattern.
Recently I found I always write code like this pattern:
result = []
result = if abc, do: [abc | result], else: result
result = if ghi, do: [ghi | result], else: result
or
result = %{}
result = if abc, do: Map.merge(result, %{abc: abc}), else: result
result = if ghi, do: Map.merge(result, %{ghi: ghi}), else: result
These are just examples.
In actual code, the situation may be more complex but follow the same pattern.
Is there an elegant solution to handle such scenarios?
Yours seems fine, but another way you could consider is using pipes:
result
|> maybe_abc(args)
|> maybe_ghi(args)
...
def maybe_abc(result, %{matching: true} = args), do: [args | results]
def maybe_abs(result, _args), do: results
def maybe_ghi(result, %{matching: true} = args), do: [args | results]
def maybe_ghi(result, _args), do: results
1 Like
Assuming abc
and ghi
are nil
(for the if
to fail)… I would do something like:
[abc, bcd, cde, efg, fgh, ghi]
|> Enum.reject(&is_nil/1)
|> Kernel.++(result)
for maps:
%{abc: abc, bcd: bcd, cde: cde, efg: efg}
|> Map.reject(fn {_k, v} -> is_nil(v) end)
|> Map.merge(result)
Enum.reduce/3
is another approach.
data = [abc, ghi, ...]
result = Enum.reduce(data, [], fn x, acc -> if x, do: [x | acc], else: acc end)
# or with a helper function and pattern matching
result = Enum.reduce(data, [], fn x, acc -> maybe_prepend(acc, x) end)
def maybe_prepend(result, data) when is_nil(data), do: result
def maybe_prepend(result, data), do: [data | result]
data = [abc: "abc", ghi: "ghi", ...]
result = Enum.reduce(data, %{}, fn x, acc -> if {key, value} = x, do: Map.put(acc, key, value), else: acc end)
# or with a helper function and pattern matching
result = Enum.reduce(data, %{}, fn x, acc -> maybe_merge(acc, x) end)
def maybe_merge(result, data) when is_nil(data), do: result
def maybe_merge(result, {key, value}), do: Map.merge(result, %{key => value})
3 Likes
Love the reduce approach! 
1 Like
Very similar to what @fceruti proposes but with only generic function:
defmodule Xyz do
def prepend_if_truthy(list, condition, value) do
if condition do
[value | list]
else
list
end
end
def test_stuff() do
[:general, :kenobi]
|> prepend_if_truthy("sure", :there) # everything that's not `nil` or `false` is "truthy"
|> prepend_if_truthy(nil, "hmmm")
|> prepend_if_truthy(false, "nope")
|> prepend_if_truthy(is_number(0.0), :hello)
end
end
Then Xyz.test_stuff()
will yield [:hello, :there, :general, :kenobi]

for
is also pretty nice for these:
for item <- list, item, do: item
for {k, v} <- item, v, into: result, do: {k, v}
8 Likes
Maybe one day InTheFarFuture™, for example a few weeks before I kick the bucket, I’ll have finally remembered that Collectable
exists.

1 Like
This is truly remarkable, and I am disappointed that I was not previously aware of this. 