build a new map from 2 differents maps using Enum.map and Enum.filter

Hello peolpe,

simple question, I have these 2 maps

[{"Africa", 1}, {"America", 2}, {"Europe", 3}]

[{"Congo", 1, 1}, {"France", 2, 3}, {"Spain", 3, 3}, {"USA", 4, 2}, {"Egypt", 5, 1}]

I want to use Enum.map and Enum.filter to get below result

[
      "Africa": [{"Congo", 1},{"Egypt", 1}], 
      "America": [{"USA", 4}], 
      "Europe": [{"France", 2}, {"Spain", 3}]
]

I have tried with the below combination but Im not getting the expected result. Can you give some help?

Enum.map(
[{"Africa", 1}, {"America", 2}, {"Europe", 3}],
fn {continent_name, continent_id} ->
Enum.filter(
[{"Congo", 1, 1},{"France", 2, 3},{"Spain", 3, 3},{"USA", 4, 2},{"Egypt", 5, 1}],
fn {country_name, country_id, country_continent_id} ->
if continent_id == country_continent_id do
[continent_name: [{country_name, country_id}]]
end
end)
end
)

Hello and welcome…

It’s not exactly what You want… but I don’t have time to polish :slight_smile:

iex> l1 = [{"Africa", 1}, {"America", 2}, {"Europe", 3}]
iex> l2 = [{"Congo", 1, 1},{"France", 2, 3},{"Spain", 3, 3},{"USA", 4, 2},{"Egypt", 5, 1}]
iex> Enum.map(l1, fn {name, id} -> {name, Enum.filter(l2, fn {_cname, _cid, conid} -> id == conid end)} end) 
[
  {"Africa", [{"Congo", 1, 1}, {"Egypt", 5, 1}]},
  {"America", [{"USA", 4, 2}]},
  {"Europe", [{"France", 2, 3}, {"Spain", 3, 3}]}
]

Hint, You need to remove continent_id from the filtered list…

First of all they are not a maps. Please read:

Here is my proposition:

defmodule Example do
  def sample(continents, countries) when is_list(continents) and is_list(countries) do
    grouped_countries = Enum.group_by(countries, &elem(&1, 2), &Tuple.delete_at(&1, 2))
    Enum.map(continents, fn {name, id} -> {name, grouped_countries[id] || []} end)
  end
end

TIP: index in Tuple is counted from 0, so it’s why I’m using 2 as element index.

I’m using here Enum.group_by/3 instead. It will create a map of key (which is generated by 2nd argument) and value which is generated by 3rd argument (Tuple without element with index 2).

Helpful resources:

  1. elem/2
  2. ||/2
  3. Enum.group_by/3
  4. Enum.map/2
  5. Tuple.delete_at/2
2 Likes

I am back to my computer… This was my initial thought.

iex> Enum.map(l1, fn {name, id} ->
  filtered = Enum.filter(l2, fn {_cname, _cid, conid} -> id == conid end)
  {name, Enum.map(filtered, fn {cname, cid, _conid} ->
    {cname, cid}
  end)}
end)
[
  {"Africa", [{"Congo", 1}, {"Egypt", 5}]},
  {"America", [{"USA", 4}]},
  {"Europe", [{"France", 2}, {"Spain", 3}]}
]

I have a bit of a reputation for liking for comprehensions, and this is a fun Saturday diversion from other duties, so how about a nested for comprehension? :grin:

iex> continents = [{"Africa", 1}, {"America", 2}, {"Europe", 3}]
iex> countries = [{"Congo", 1, 1}, {"France", 2, 3}, {"Spain", 3, 3}, {"USA", 4, 2}, {"Egypt", 5, 1}]
iex> for {ct_na, ct_no} <- continents, do: {ct_na, (for {na, no, ^ct_no} <- countries, do: {na, no})}
[
  {"Africa", [{"Congo", 1}, {"Egypt", 5}]},
  {"America", [{"USA", 4}]},
  {"Europe", [{"France", 2}, {"Spain", 3}]}
]
1 Like