Home > Back-end >  Add items to dict in elixir
Add items to dict in elixir

Time:11-02

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?

CodePudding user response:

In the case you want to build a structure within a "loop", you can most of the times reach for Enum.reduce/3:

Enum.reduce(sorted_slides, %{}, fn slide, acc -> 
  Enum.into(slide.visual_events, acc, fn event -> 
    {event.entity_id, event}
  end)
end)

The inner loop could be implemented with Enum.reduce/3 as well, but Enum.into/3 makes it slightly more compact.

Enum.each/2 is only meant to perform side-effects (like printing) but doesn't return any actual result, it just always returns :ok.

I guess that scoping works different from some other languages here.

Exactly, in Elixir you don't mutate existing structures, you create new structures and need to pass them around. This is typically the case of our acc accumulator above.

Side note: in Elixir, these are not called dicts but maps. There have been deprecated dicts structure in the past.

CodePudding user response:

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"}
}
  • Related