dictionarydata-structuresiteratorelixirscoping

Add items to dict in elixir


I have a nested dict in elixir, from which I want to save the latest items in a new dict.

sorted_slides = [
  %{
    id: 1,
    visual_events: [
      %{entity_id: 1, payload: "abc"},
      %{entity_id: 2, payload: "def"}
    ]
  },
  %{
    id: 2,
    visual_events: [
      %{entity_id: 2, payload: "yui"},
      %{entity_id: 3, payload: "def"},
      %{entity_id: 4, payload: "ghi"},
    ]
  },
  %{
    id: 3,
    visual_events: [
      %{entity_id: 2, payload: "ert"},
      %{entity_id: 4, payload: "poi"},
    ]
  }
]

dict = %{}

Enum.each(sorted_slides, fn slide -> 
  Enum.each(slide.visual_events, fn ve -> 
    eid = ve.entity_id
    dict = Map.put(dict, eid, ve)
    IO.inspect(dict)
  end)
end)

IO.inspect(dict)

My original data structure contains items that may be overwritten by newer items. I want the new dict to be:

dict = %{
  1 => %{entity_id: 1, payload: "abc"},
  2 => %{entity_id: 2, payload: "ert"},
  3 => %{entity_id: 3, payload: "def"},
  4 => %{entity_id: 4, payload: "poi"}
}

I want the dict to save the changes made to it by each iteration, but I guess that scoping works different from some other languages here.

How would I achieve this in Elixir?


Solution

  • You can use Enum.flat_map/2 to extract the inner elements, and Map.new/2 to construct a new map from those elements. Map.new/2 will ensure the latest element prevails when there are duplicate keys.

    sorted_slides
    |> Enum.flat_map(fn %{visual_events: items} -> items end)
    |> Map.new(fn %{entity_id: id} = map -> {id, map} end)
    

    Result:

    %{
      1 => %{entity_id: 1, payload: "abc"},
      2 => %{entity_id: 2, payload: "ert"},
      3 => %{entity_id: 3, payload: "def"},
      4 => %{entity_id: 4, payload: "poi"}
    }