How to edit and sort index of nested map in a list

Hello, I have a list like this:

[
    %{
      index: 1,
      params: %{
        id: "test1",
        title: "test of one",
        width: "col-sm-12"
      },
      type: "Heading"
    },

    %{
      index: 3,
      params: %{
        id: "test3",
        title: "test of one",
        width: "col-sm-12"
      },
      type: "Heading"
    },

    %{
      index: 5,
      params: %{
        id: "test5",
        title: "test of one",
        width: "col-sm-12"
      },
      type: "Heading"
    },
]

and I want to change all index number and sort them but with new index which starts 0 like this

[
    %{
      index: 0,
      params: %{
        id: "test1",
        title: "test of one",
        width: "col-sm-12"
      },
      type: "Heading"
    },

    %{
      index: 1,
      params: %{
        id: "test4",
        title: "test of one",
        width: "col-sm-12"
      },
      type: "Heading"
    },

    %{
      index: 2,
      params: %{
        id: "test5",
        title: "test of one",
        width: "col-sm-12"
      },
      type: "Heading"
    },
]

I need this because maybe a map is deleted in a list and the map deleted index will be null and I want to re-index all the map after every deleting

Thanks

Hi @shahryarjb, what have you tried so far?

1 Like

many wrong ways :)) . like create a Enum.count of original list and Enum.shuffle‍ and put them in a Enum.map :cold_face: or tried Edit each map in Enum.map, but I couldn’t create a :index numbers and use it to Map.merge

Enum.map(new_blocks, fn item ->
        Map.merge(item, %{index: number})
end)

I cant get a number in a loop and + 1 every loop

I think this should be this :rofl: :shushing_face: :shushing_face:

new_blocks
    |> Enum.with_index
    |> Enum.map(fn {x, index} ->
      Map.merge(x, %{index: index})
    end)
    |> IO.inspect()
1 Like

Like this?

defmodule MyApp.Indexer do
  def put_index(list, index \\ 0)

  def put_index([], _) do
    []
  end

  def put_index([head | tail], index) do
    [Map.put(head, :index, index) | put_index(tail, index + 1)]
  end
end

Check out the Enum.with_index source: https://github.com/elixir-lang/elixir/blob/v1.11.2/lib/elixir/lib/enum.ex#L3152

1 Like

If you wanted to use with_index you could do this but you’d be going through those more than you need to:

Enum.with_index(list) |> Enum.map(fn {x, index} -> Map.put(x, :index, index) end)
2 Likes

@chasers offers a good solution. Another way it could be done is to use Enum.map_reduce/3

For more info h Enum.map_reduce/3 in your terminal

  def map_indexer(list) when is_list(list) do
    Enum.map_reduce(list, 0, &update_index/2)
    |> elem(0)
  end

  def update_index(map, index) when is_map(map) do
    {Map.put(map, :index, index), index + 1}
  end
1 Like

Here’s a one-liner.

Enum.reduce(list, {0, []}, fn element, {index, list_acc} -> {index + 1, [%{element | index: index} | list_acc]} end)
|> elem(1)
|> Enum.reverse()
2 Likes

Another one liner :slight_smile:

for {el, index} <- list |> Enum.sort(& &1.index <= &2.index) |> Enum.with_index(), 
  do: %{el | index: index}

or

list 
|> Enum.sort(& &1.index <= &2.index) 
|> Enum.with_index() 
|> Enum.map(& %{elem(&1, 0) | index: elem(&1, 1)})
1 Like

The only “issue” with this approach is how many times you are traversing the list.