Edit value of map that is inside a list

Hello. I have a problem with editing the value of a map that is inside a list.

For example I have the following list:

list = [ a: %{ index: 0, amount: 0 }, b: %{ index: 0, amount: 0 } ]

I use Enum.count(list) to count the elements and I get the first one with Enum.at(list,0) but the problem is the output. The returned result of Enum.at is: {:a, %{amount: 0, index: 0}}.
I am not sure how I can edit the value of :a , I tried many different ways but couldn’t manage to finish it. For contrast, I would like to set :amount => 2 or some different value.

As I understand you, you can use put_in/3:

iex(1)> l = [a: %{value: 0}]
iex(2)> put_in(l, [:a, :value], 2)
[a: %{value: 2}]
1 Like

I want to get the position of a map in the list then to edit the map on this position.

While this is a list, it might make more sense to work with it as a keyword list. You can use Keyword.update/4 or Keyword.update!/3 to update the value for a key with a function. In this case, your update function would be a map update putting the value 2 for :amount

Keyword.update!(list, :a, fn(map) -> Map.put(map, :amount, 2) end)

Even though in a list position matters, its semantically useless in key-value lists. Why assign a key first when you actually identify the items by position?

So I do not think its good practice to do it by index.

It should be roughly doable by the following code though (which is untested):

def change_at(enum, pos, updater) do
  {front, [item | tail]} = Enum.split(enum, pos)
  front ++ [updater.(item) | tail]

This is a quick and dirty hack which should do what you want, but it is not very efficient, also it will crash when the given index is too large.

updater is meant to be a function, which takes the old value of the item and returns the new. My example from above could then look like this (still untested and output is written as I assume it to happen!):

iex(1)> l = [a: %{value: 0}]
iex(2)> change_at(l, 0, &({elem(&1, 0), put_in(elem(&1, 1), [:value], 2)))
[a: %{value: 2}]

He explicitely asked to work on the list by index, this rules out a KW list, as it only allows access by key.

The key/value pair is still being retrieved, so it can still be manipulated that way. They’re just being retrieved by position. So, e.g.

{key, _} = Enum.at(list, index)
Keyword.update!(list, key, fn(map) -> Map.put(map, :amount, 2) end)

would update the :amount for the entry at the given index. Or the value can be manipulated directly

{key, value} = Enum.at(list, index)
new_value = Map.put(value, :amount, 2)
Keyword.put(list, key, new_value)
1 Like

Thats a nice idea, actually to get the key first and then use it to update the KW-list. I do fear though, that the initial input given by the OP is a KW-list only by accident and that a Key-Value-association is not guaranteed.

I do fear though, that the initial input given by the OP is a KW-list only by accident and that a Key-Value-association is not guaranteed.

That’s true. If the “key” in the list is not an atom but still unique, the same thing can be accomplished with List.keyreplace/4. If there is no uniqueness and it has the entries have to be addressed by index, something like this should also work:

List.update_at(list, index, fn({key, value}) -> {key, Map.put(value, :amount, 2)} end)


List.insert_at(list, index, {key, new_value})

with new_value like before.

I think I found a solution that works!
It is something like that in my mind:

def func(name) do
  index =0
  if Map.has_key?(a, name) == true do
    index = index + 1

Thank you both for helping me :slight_smile:

I’m sensing an X Y Problem situation - i.e. what is the larger problem that you are actually trying to solve?

You do understand that it is impossible to edit any value? Because all values are immutable, i.e. no value can be changed.

So the best you can do is to create an entirely new list which includes the modifications that you want so that you can use the new list instead of the old list (which is what the answers shown so far are doing).