How to deal with this pattern elegant?

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:

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]

:smiley:

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. :rofl: