zack_falcon
zack_falcon

Reputation: 4376

Elixir: Update a specific value in a list of maps

Given the following list of maps:

ball_prop_list = 
[
  %{"id" => "cue", "is_idle" => true, "velocity_x" => 0.0, "velocity_z" => 0.0, "x" => -15.0, "z" => 0.0},
  %{"id" => "ball_1", "is_idle" => true, "velocity_x" => 0.0, "velocity_z" => 0.0, "x" => 15.0, "z" => 0.0},
  %{"id" => "ball_2", "is_idle" => true, "velocity_x" => 0.0, "velocity_z" => 0.0, "x" => 17.0, "z" => 1.1},
  %{"id" => "ball_3", "is_idle" => true, "velocity_x" => 0.0, "velocity_z" => 0.0, "x" => 17.0, "z" => -1.1}
]

How do I go about changing, say, just the velocity_x and velocity_y of "id" => "cue", and keeping everything else the same?

I've only gotten as far as trying to create a new list (which was not necessarily my goal) with updated values, such as the following:

new_prop_list = Enum.map(balls_json_list, fn(val) -> 
  if val["id"] == "cue" do
    Map.put(val, "velocity_x" velocity_vector[:x])
    Map.put(val, "velocity_z" velocity_vector[:z])
  end
end)

But this didn't work out, probably because my maps do not use atom keys - every other example I've seen so far uses atom keys, or adds a new key and value, etc.

Is there anything else I can try?

Upvotes: 2

Views: 2776

Answers (2)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

There is extremely underrated Access in that is way more powerful, especially when it comes to deep updates.

Even despite in this particular case the straightforward Map.put/3 might seem easier, I’d post the solution with update_in/3 using Access. Note that it might be used to modify a deeply nested structure on any level, unlike Map.put.

update_in(
  ball_prop_list,
  [Access.filter(&match?(%{"id" => "cue"}, &1))],
  &Map.merge(&1, %{"velocity_x" => :new_velocity})
)

Upvotes: 8

Aetherus
Aetherus

Reputation: 8888

Map.put/3 does not modify the map in-place (because no data structure in Elixir can be modified), but creates a new map.

Also, you missed 2 commas.

new_ball_prop_list = Enum.map(ball_prop_list, fn
  %{"id" => "cue"} = map ->
    map
    |> Map.put("velocity_x", velocity[:x])
    |> Map.put("velocity_y", velocity[:y])
  other_map ->
    other_map
end)

You can also try Map.merge/2 or %{map | k1 => v1, k2 => v2}

Upvotes: 4

Related Questions