Push into list or modify the list based on key from a list

I want to know how do this in functional way.

input list

[%{
"id" => 1,
"value" => "x"
},
%{
"id" => 1
"value" => "y"
},
%{
"id" => 2
"value" => "z"
}]

resulted list


[%{
"id" => 1,
"values" => ["x","y"]
},
%{
"id" => 2,
"values" => ["z"]
}
]

Try this code:

defmodule Example do
  def sample(data), do: Enum.reduce(data, [], &sample/2)

  defp sample(%{"id" => id, "value" => value}, [%{"id" => id, "values" => values} | tail]) do
    [%{"id" => id, "values" => values ++ [value]} | tail]
  end

  defp sample(map, [head | tail]), do: [head | sample(map, tail)]

  defp sample(%{"id" => id, "value" => value}, []), do: [%{"id" => id, "values" => [value]}]
end

and this test file:

defmodule ExampleTest do
  use ExUnit.Case

  test "Example.sample/1" do
    input = [
      %{"id" => 1, "value" => "x"},
      %{"id" => 1, "value" => "y"},
      %{"id" => 2, "value" => "z"}
    ]

    expected = [
      %{"id" => 1, "values" => ["x", "y"]},
      %{"id" => 2, "values" => ["z"]}
    ]

    assert Example.sample(input) === expected
  end
end

btw. It would be nice if next time you would give valid Elixir example data (as you have missed few , characters) and format them using builtin Elixir formatter (mix format task).

1 Like

Enum module is your friend

iex> list = [%{"id" => 1, "value" => "x"}, %{"id" => 1, "value" => "y"}, %{"id" => 2, "value" => "z"}]
iex> Enum.group_by(list, fn x -> x["id"] end) 
|> Enum.map(fn {k, v} -> %{k => Enum.map(v, & &1["value"])} end)
[%{1 => ["x", "y"]}, %{2 => ["z"]}]
2 Likes

I love a recursive solution for its didactic value, but in production code I’m more likely to rely upon the standard library and would do it something like this:

input
|> Enum.group_by(&(&1["id"]))
|> Enum.map(fn {id, list} -> %{"id" => id, "value" => Enum.map(list, &(&1["value"]))} end)

edit: Haha @kokolegorille you got in a minute ahead of me with the same basic approach

3 Likes

We share the same idea :slight_smile:

1 Like