How to filter a map grouping by a column only if not null

Basically I want to filter this kind of list:

[
  %{name: "gigi", priority: 2, value: 2},
  %{name: "gigi", priority: 1, value: 1},
  %{name: nil, priority: 1, value: 1},
  %{name: nil, priority: 1, value: 2}
]

and get this list as a result:

[
  %{name: "gigi", priority: 2, value: 2},
  %{name: nil, priority: 1, value: 1},
  %{name: nil, priority: 1, value: 2}
]

The are already sorted desc by name and priority.

I tried this code, it works but does not look nice at all to me:

arr
    |> Enum.group_by(&Map.get(&1, :name))
    |> Enum.map(fn
      {nil, v} -> v
      {_, v} -> List.first(v)
    end)
    |> List.flatten()

Is there a more optimum way to filter the array?

I think Enum.uniq_by/2 could be very useful for this problem if you just want to keep one element of each name then your function to determine uniqueness could be the name, except when it’s nil.

I tried your list with this function and it worked:

Enum.uniq_by(list, fn e -> if is_nil(e.name), do: e, else: e.name end)

However, this takes for granted that the elemets that have name nil are not completely equal between each other.

https://hexdocs.pm/elixir/Enum.html#uniq_by/2-example

1 Like

The problem is that it excludes the nil too:

Enum.uniq_by(arr, fn %{name: name} -> name end)
[%{name: "Gigi", priority: 1, value: 1}, %{name: nil, priority: 1, value: 1}]

I tried your list with this function and it worked:

Enum.uniq_by(list, fn e -> if is_nil(e.name), do: e, else: e.name end)

However, this takes for granted that the elemets that have name nil are not completely equal between each other.

Looks better than my solution but still that if…

You could pattern match if you don’t want to use if

Enum.uniq_by(list, fn
  %{name: nil} = e -> e
  e -> e.name
end)
2 Likes