Search code examples
listfilterelixirenumerable

Filter single item from a List


Given:

iex(9)> list_of_maps = [%{"a" => 1, "b"  => 2, "c" => 3},%{"a" => 66, "b"  => 1, "c" => 9},%{"a" => 66, "b"  => 20, "c" => 8}]

I can do:

iex(10)> Enum.filter(list_of_maps, &(&1["a"] == 1))                                                                                                                                                                
Enum.filter(list_of_maps, &(&1["a"] == 1))                                                                                                                                                                         
[%{"a" => 1, "b" => 2, "c" => 3}]

However now comes the part I dread writing in every language - getting the first value of this list to extract the single item.

Is there some standard function in elixir that filters a list, returning single item if there is only one item after a filter is applied, or a list of items if there are numerous items returned after the filter is applied? Like:

iex(11)> Enum.filterr(list_of_maps, &(&1["a"] == 1))                                                                                                                                                                
Enum.filter(list_of_maps, &(&1["a"] == 1))                                                                                                                                                                         
%{"a" => 1, "b" => 2, "c" => 3}

iex(12)> Enum.filterr(list_of_maps, &(&1["a"] == 66))                                                                                                                                                                
Enum.filter(list_of_maps, &(&1["a"] == 66))                                                                                                                                                                         
[%{"a" => 66, "b"  => 1, "c" => 9},%{"a" => 66, "b"  => 20, "c" => 8}]]

Solution

  • Find one item from a List

    If you want to filter a list to get only one item, use Enum.find/2:

    Enum.find(list_of_maps, fn m -> m["a"] == 1 end)
    

    Get one or a list of matches

    To handle both cases, pattern matching is the way to go:

    defmodule MyEnum do
      def filter(list, fun) do
        list
        |> Enum.filter(fun)
        |> normalize
      end
    
      defp normalize([item]), do: item
      defp normalize(list),   do: list
    end
    

    You can then use it like this:

    MyEnum.filter(list_of_maps, &(&1["a"] == 1))
    

    It will return a list if there are multiple matches or the map itself if there's only one match.