Rewrite filter into pipe operators in Elixir

Viewed 356

I am very new to Elixir and am trying to rewite this:

kids = ["Mike", "Paula", "Julia", "Phil"]
Enum.filter(kids, fn(n) -> String.contains? n, "i" end)
# results in: ["Mike", "Julia", "Phil"]

into something like this

kids |> Enum.filter String.contains? "i"

Won't work. - What am I missing?

3 Answers

That's very close, you can go with it step by step.

  1. Move kids "before" |>:

    kids |> Enum.filter(fn(n) -> String.contains?(n, "i") end)

  2. Use capture operator to shorten the filtering function:

    kids |> Enum.filter(&String.contains?(&1, "i"))

Hope that helps!

Trying it on iex gives us the error:

iex(4)> kids = ["Mike", "Paula", "Julia", "Phil"]                 
["Mike", "Paula", "Julia", "Phil"]
iex(5)> kids |> Enum.filter String.contains? "i" 
warning: parentheses are required when piping into a function call. For example:

    foo 1 |> bar 2 |> baz 3

is ambiguous and should be written as

    foo(1) |> bar(2) |> baz(3)

Ambiguous pipe found at:
  iex:5

** (UndefinedFunctionError) function String.contains?/1 is undefined or private. Did you mean one of:

      * contains?/2

    (elixir) String.contains?("i")

The error is because String.contains?/2 expects 2 arguments and only one was given. Also the second argument to Enum.filter/2 should be a function. So, to get rid of the error you could write:

iex(1)> kids = ["Mike", "Paula", "Julia", "Phil"]
["Mike", "Paula", "Julia", "Phil"]
iex(2)> kids |> Enum.filter fn(n) -> String.contains? n, "i" end
warning: parentheses are required when piping into a function call. For example:

    foo 1 |> bar 2 |> baz 3

is ambiguous and should be written as

    foo(1) |> bar(2) |> baz(3)

Ambiguous pipe found at:
  iex:2

["Mike", "Julia", "Phil"]

To make the pipeline unambiguous, and in the process get rid of the warning, simply add parentheses around the function call:

iex(3)> kids = ["Mike", "Paula", "Julia", "Phil"]               
["Mike", "Paula", "Julia", "Phil"]
iex(4)> kids |> Enum.filter(fn(n) -> String.contains? n, "i" end)
["Mike", "Julia", "Phil"]

You can also use comprehensions with a filter:

for kid <- kids, String.contains?(kid, "i"), do: kid
["Mike", "Julia", "Phil"]
Related