Daniel
Daniel

Reputation: 1110

How NOT to use all arguments when using capture operator?

I'm trying to understand capture operator and where it can be used. Here is working example:

%{name: "Daniel", dob: 2016, email: "[email protected]"}
|> Enum.filter(fn({_k, v}) -> is_binary(v) end)
|> Enum.into(%{})

which returns

%{email: "[email protected]", name: "Daniel"} .

Now I'm trying to use capture operator inside Enum.filter/2 where I'm using the second element from map pair.

%{name: "Daniel", dob: 2016, email: "[email protected]"}
|> Enum.filter(&is_binary(&2))
|> Enum.into(%{})

and the iex console says

** (CompileError) iex:2: capture &2 cannot be defined without &1
    (elixir) src/elixir_fn.erl:118: :elixir_fn.do_capture/4
    (elixir) expanding macro: Kernel.|>/2
             iex:2: (file)

The result is somewhat expected. My question is how to bypass this, how can I use second element as argument without using the first one?

Upvotes: 1

Views: 854

Answers (1)

Dogbert
Dogbert

Reputation: 222128

Like the error message says, you cannot use just the second argument using the capture operator, BUT, the function fn({_k, v}) -> is_binary(v) end only takes one argument, which is a 2 element tuple, not 2 arguments. You can use elem/2 if you don't want to name it:

iex(1)> %{name: "Daniel", dob: 2016, email: "[email protected]"} |>
...(1)> Enum.filter(&is_binary(elem(&1, 1))) |>
...(1)> Enum.into(%{})
%{email: "[email protected]", name: "Daniel"}

For this particular use case, you can also use :maps.filter/2. It's shorter than your original code and likely more efficient:

iex(2)> :maps.filter fn k, v -> is_binary(v) end, %{name: "Daniel", dob: 2016, email: "[email protected]"}
%{email: "[email protected]", name: "Daniel"}

Upvotes: 2

Related Questions