2

When using the pipe operator in Elixir, is it possible to be selective/specific about the output that is passed from the first function to the second?

For example, I'm processing a map in three steps - I need to drop two keys, update the value of a different key, and finally pop another key, whose value I need to use later on in my code. Here's an illustration of what I'd ideally be able to achieve:

for r <- records do
  {scope, record} = 
    Map.drop(r, [:__struct__, :__meta__])
    |> Map.get_and_update(:id, fn current_value -> 
      {current_value, String.replace(current_value, "join", "scope", global: false)}
    end)
    |> Map.pop(:user_id)

  # SOME OTHER STUFF...

end

So that'd drop the :__struct__ and :__meta__ keys, update the value of the :id key by replacing the word "join" with "scope", and then pop the :user_id key off, leaving me with {scope, record}, where scope is the value of the popped key and record is the modified map. The code, as is, does not work, of course.

Here's a piece of sample data:

records = [
  %{__struct__: "foo", __meta__: "bar", id: "resource_join:1234", user_id: "user:1234"},
  %{__struct__: "foo", __meta__: "bar", id: "resources_join:5678", user_id: "user:5678"},
]

The first step in this process works, because drop/2 returns just the modified map, which is the correct input for the first parameter to get_and_update/3. However, get_and_update/3 returns a tuple where the first value is the value that that the function replaced and the second value is the modified map. This is where the pipe chain fails, because pop/2 expects a map as the first argument, and here it's receiving a tuple.

Is there a way that I can control the output of get_and_update/3 so that only the second value from the returned tuple is passed through the pipe to pop/2? I've been able to get the output that I need by breaking this chain up into several pieces, but if it were at all possible to chain these together, I'd like to.

5
  • Consider including data example, so anyone could run your code in console Commented Oct 20, 2017 at 13:05
  • Have you looked into using with in combination of what you have there? Commented Oct 20, 2017 at 13:06
  • Do you mean like adding |> elem(1) before |> Map.pop(...)? There's also Map.update if you don't care about the old value in Map.get_and_update. Commented Oct 20, 2017 at 13:08
  • @Dogbert right on, thanks for pointing out elem() - didn't know about that. I'll probably end up going with update(), but elem() technically answers my question. Commented Oct 20, 2017 at 13:10
  • @denis.peplin - good call, I've added a piece of sample data. Commented Oct 20, 2017 at 13:23

1 Answer 1

2

Since you want to discard the first element of the tuple and only pass the second to Map.pop, you can add |> elem(1) to the pipeline:

|> Map.get_and_update(:id, fn current_value -> 
  {current_value, String.replace(current_value, "join", "scope", global: false)}
end)
|> elem(1)
|> Map.pop(:user_id)

Also, Map.get_and_update + elem(1) can be replaced with just Map.update, which doesn't return the old value of the key:

|> Map.update(fn current_value -> 
  String.replace(current_value, "join", "scope", global: false)
end)
|> Map.pop(:user_id)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.