zaire crypto
zaire crypto

Reputation: 45

Using Enum.map to create a key map

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
)

Upvotes: 1

Views: 140

Answers (4)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

Kernel.SpecialForms.for/1 comprehension is extremely underrated. This is probably the easiest way to go.

(for {ct, cti} <- continents,
     {cy, cyi, cycti} <- countries, cti == cycti,
  do: {String.to_atom(ct), {cy, cyi}})
|> Enum.reduce([], fn {k, v}, acc ->
  Keyword.update(acc, k, [v], fn l -> [v | l] end)
end)

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

On the first step we go through continents, then through countries and filter the countries by continents inplace.

Upvotes: 1

edwinallenz
edwinallenz

Reputation: 340

This doesn't use filter, but let me know if this works for you.

continents = [{"Africa", 1}, {"America", 2}, {"Europe", 3}]
countries = [{"Congo", 1, 1}, {"France", 2, 3}, {"Spain", 3, 3}, {"USA", 4, 2}, {"Egypt", 5, 1}]

##Place countries by continent id in a map
countries_by_continent = Enum.reduce(countries,%{}, fn({name, country_id, continent_id}, countries_map) ->
  case Map.get(countries_map, continent_id) do
    nil ->   Map.put(countries_map, continent_id, [{name, country_id}])
    current_countries -> Map.put(countries_map, continent_id, current_countries ++ [{name, country_id}])
  end
end)

Enum.map(continents, fn({continent_name, continent_id}) ->
  ["#{continent_name}": Map.get(countries_by_continent, continent_id)]
end)

Upvotes: 0

Adam Millerchip
Adam Millerchip

Reputation: 23147

Here's my attempt:

continents =
  [{"Africa", 1}, {"America", 2}, {"Europe", 3}]
  |> Map.new(fn {a, b} -> {b, String.to_atom(a)} end)

[
  {"Congo", 1, 1},
  {"France", 2, 3},
  {"Spain", 3, 3},
  {"USA", 4, 2},
  {"Egypt", 5, 1}
]
|> Enum.group_by(&elem(&1, 2), &Tuple.delete_at(&1, 2))
|> Enum.map(fn {id, list} -> {continents[id], list} end)

Output:

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

Upvotes: 1

TheAnh
TheAnh

Reputation: 2813

I would like to use Enum.group_by/3

iex()> cons = [{"Africa", 1}, {"America", 2}, {"Europe", 3}]
iex()> countries = [{"Congo", 1, 1}, {"France", 2, 3}, {"Spain", 3, 3}, {"USA", 4, 2}, {"Egypt", 5, 1}]
iex()> countries = Enum.group_by(countries, fn {_, _, group_key} -> group_key end, fn {country, val, _} -> {country, val} end) |> Enum.into([])
[
  {1, [{"Congo", 1}, {"Egypt", 5}]},
  {2, [{"USA", 4}]},
  {3, [{"France", 2}, {"Spain", 3}]}
]
iex()> for {con, key_to_match} <- cons, {k, grouped_country} <- countries, key_to_match == k do
...()> [{String.to_atom(con), grouped_country}]
...()> end
[
  [Africa: [{"Congo", 1}, {"Egypt", 5}]],
  [America: [{"USA", 4}]],
  [Europe: [{"France", 2}, {"Spain", 3}]]
]

Upvotes: 1

Related Questions