How to filter a list of nils

I have sometimes list with elements of different type.
for example nil and tuple
the thing is that the filter function is a bit long. I wonder if this can be written shorter. particularly because i don’t care about failing cases.

Enum.filter(list_of_nils_and_tuples, fn elem ->
        if(is_nil(elem)) do
          false
        else
          {id, _v} = elem
            some_complicated_test(id)
        end
      end)

The question is maybe more about how to avoid if else structures in elixir?

Pattern matching to the rescue!

Enum.filter(list_of_nils_and_tuples, fn 
  nil -> false
  {id, _v}  -> some_complicated_test(id)
end)
3 Likes

ah thanks forgot about pattern matching in unnamed functions.
I guess there is no chance to get rid of the
fn nil → false end
part, right?
I am thinking about it because the documentation for filter says:

Blockquote
Filters the enumerable , i.e. returns only those elements for which fun returns a truthy value.

So I thought it might maybe be possible to let things be “falthy” without declaring them false.

Another option is to to use a comprehension:

for {id, _v} = nil_or_tuple <- list_of_nils_and_tuples, not is_nil(nil_or_tuple), do: some_complicated_test(id)
1 Like

You can, but You also apply a transformation at the same time…

list_of_nils_and_tuples
|> Enum.filter(& &1)     # drop falsy value
|> Enum.map(& some_complicated_test(elem(&1, 0)))

You can also define some_complicated_test to return false when nil is passed, and use it as the filter function.

2 Likes

You can also filter/reject multiple times, though you may want to use stream instead of enum for longer lists.

list_of_nils_and_tuples
|> Enum.reject(&is_nil/1)
|> Enum.filter(fn {id, _v} -> some_complicated_test(id) end)
1 Like

I just had to do something similar and was looking for something like Enum.compact().
I ended up using a comprehension with pattern matching as well, but you don’t need the match operator (=) or the guard. The {id, _v} itself is already doing the work. Example:


iex(0)> list = [nil, nil, {1,2}, nil, {3,4}]
[nil, nil, {1, 2}, nil, {3, 4}]

iex(1)> for {id, _v} <- list, do: id
[1, 3]
2 Likes