jimmbraddock
jimmbraddock

Reputation: 887

Get a first argument when pipe operator used

Pipe operator is very nice thing. For example, I wish to use enumerable into Enum.reduce or something yet:

foo |> bar(...) |> Enum.reduce([], fn x, acc -> acc ++ [Enum.at(<enumerable>, x)] end)

Then I am compelled to write:

enumerable = foo |> bar(...)
Enum.reduce(enumerable, [], fn x, acc -> acc ++ [Enum.at(enumerable, x)] end)

May be exist syntax sugar for the first argument when used pipe operator as well as &1 in anonymous function?

Upvotes: 2

Views: 2787

Answers (1)

tkowal
tkowal

Reputation: 9289

It would be a good idea to simply move the whole Enum.reduce to its own function that takes one argument and name it, so everyone can understand what is it doing. rearrange is not the most descriptive name, but I am not sure how is this procedure used in your app.

def rearrange(enumerable) do
  Enum.reduce(enumerable, [], fn x, acc -> acc ++ [Enum.at(enumerable, x)] end)
end

foo |> bar(...) |> rearrange

Another solution would be to use anonymous function as you mentioned in your question. It is worse, but I mention it for completeness.

&(Enum.reduce(&1, [], fn x, acc -> acc ++ [Enum.at(&1, x)] end))

This looks really convoluted. You have anonymous function inside anonymous function, but it shows, that you can use &1 more than one time in your function.

Lastly, acc ++ [thing] is really bad idea, because it recreates the list in memory from scratch every time. It is O(n). Enum.at is also linear and so is Enum.reduce, so this function is O(n^3).

You can do fn x, acc -> [Enum.at(enumerable, x) | acc] end instead and reverse the result at the end.

def rearrange(enumerable) do
  enumerable
  |> Enum.reduce([], fn x, acc -> [Enum.at(enumerable, x) | acc] end)
  |> Enum.reverse
end

[thing | acc] is O(1). This will make the whole procedure O(n^2) and that is reasonable time for what it is doing.

Upvotes: 3

Related Questions