2
votes

Let's assume that we have the following:

list = 
[
  %{"id" => 1, "name" => "Alice"},
  %{"id" => 2, "name" => "Bob"},
  %{"id" => 3, "name" => "Charlie"}
]

In order to add key type with a value person to each element we could do: list |> Enum.map(fn x -> Map.put(x, "type", "person") end) which will result in:

[
  %{"id" => 1, "name" => "Alice", "type" => "person"},
  %{"id" => 3, "name" => "Bob", "type" => "person"},
  %{"id" => 3, "name" => "Charlie", "type" => "person"}
]

Now, let's say that we have another list:

types = ["human", "alien", "unknown"]

How to combine list and types so that the end result will be:

[
  %{"id" => 1, "name" => "Alice", "type" => "human"},
  %{"id" => 2, "name" => "Bob", "type" => "alien"},
  %{"id" => 2, "name" => "Charlie", "type" => "unknown"}
]

?

3

3 Answers

1
votes

Using Enum.zip/2 and Enum.map/2 should do the trick:

list
|> Enum.zip(types)
|> Enum.map(fn {list, type} -> Map.put(list, "type", type) end)

[
  %{"id" => 1, "name" => "Alice", "type" => "human"},
  %{"id" => 2, "name" => "Bob", "type" => "alien"},
  %{"id" => 3, "name" => "Charlie", "type" => "unknown"}
]
1
votes

While Daniel’s answer is perfectly correct, one might achieve more fine-grained control over what to do if lists are of different length with Enum.reduce/3

list
|> Enum.reduce({types, []}, fn
  e, {[], acc} -> {[], [Map.put(e, "type", "unknown") | acc]}
  e, {[t |ts], acc} -> {ts, [Map.put(e, "type", t) | acc]}
end)
|> elem(1)

or with Enum.reduce_while/3

list
|> Enum.reduce_while({types, []}, fn
  e, {[], acc} -> {:halt, {[], [Map.put(e, "type", "unknown") | acc]}}
  e, {[t |ts], acc} -> {:cont, {ts, [Map.put(e, "type", t) | acc]}}
end)
|> elem(1)
0
votes

These are both perfectly good answers, but for the sake of completion I just wanted to include two more options, which can achieve both the "zip" and "map" phases in one single pass:

  1. Writing a recursive function
  def zip_persons([], []), do: []

  def zip_persons([person | persons], [type | types]) do
    [Map.put(person, "type", type) | zip_persons(persons, types)]
  end
  1. This is still not released, but from Elixir 1.12 you will be able to use Enum.zip_with/3 for this case. I'm mentioning it in case somebody finds this post after it gets released :)
Enum.zip_with(list, types, fn person, type -> Map.put(person, "type", type) end)