Skip to content

Instantly share code, notes, and snippets.

@padde
Last active May 8, 2017 23:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save padde/a759b18bca7851e8ffaa to your computer and use it in GitHub Desktop.
Save padde/a759b18bca7851e8ffaa to your computer and use it in GitHub Desktop.
Filter a list by pattern matching in Elixir
defmodule MyEnum do
defmacro filter_matching(collection, pattern) do
quote do
Enum.filter unquote(collection), fn
unquote(pattern) -> true
_ -> false
end
end
end
end
require MyEnum
MyEnum.filter_matching [a: 1, b: 2, a: 3], {:a, _}
#=> [a: 1, a: 3]
@padde
Copy link
Author

padde commented Jan 7, 2015

This can also be elegantly done with the match?/2 macro:

Enum.filter [a: 1, b: 2, a: 3], &match?({:a, _}, &1)

this allows the re-implementation without using macros:

defmodule MyEnum  do
  def filter_matching(collection, pattern) do
    Enum.filter collection &match?(pattern, &1)
  end
end

@venkatd
Copy link

venkatd commented Oct 5, 2016

This doesn't work for me unless I'm misunderstanding the usage

iex(49)> MyEnum.filter_matching([%{a: 3}, %{b: 9}], %{a: 3})
[%{a: 3}, %{b: 9}]

@venkatd
Copy link

venkatd commented Dec 11, 2016

The following should work:

  defmacro find_matching(collection, pattern) do
    quote do
      Enum.find(unquote(collection), &match?(unquote(pattern), &1))
    end
  end
  defmacro filter_matching(collection, pattern) do
    quote do
      Enum.filter(unquote(collection), &match?(unquote(pattern), &1))
    end
  end
  defmacro reject_matching(collection, pattern) do
    quote do
      Enum.reject(unquote(collection), &match?(unquote(pattern), &1))
    end
  end
  defmacro any_matching?(collection, pattern) do
    quote do
      Enum.any?(unquote(collection), &match?(unquote(pattern), &1))
    end
  end
  defmacro all_matching?(collection, pattern) do
    quote do
      Enum.all?(unquote(collection), &match?(unquote(pattern), &1))
    end
  end
  defmacro count_matching(collection, pattern) do
    quote do
      Enum.count(unquote(collection), &match?(unquote(pattern), &1))
    end
  end

Because of the way macros work, this is impossible to do without a macro.

@JHKennedy4
Copy link

One other approach is to just write two functions, one that matches the true case, and another that matches the false case and just pass that function. Especially helpful if you want to negate some condition such as nil in a parameter.

  zip_rates
  |> Enum.filter(&zip_nil/1)

  def zip_nil { _zip, nil } do
    false
  end

  def zip_nil { _zip, _price } do
    true
  end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment