Reputation: 368
Let's say that I have a list of structs, with an id
as one of its keys:
users = [
%User{id: 1, attempts: 5},
%User{id: 2, attempts: 12},
%User{id: 3, attempts: 2}
]
What would the best way (the most elixir way) to update the number of attempts to 99 of the user with id == 2
to get a new users list:
updated_users = [
%User{id: 1, attempts: 5},
%User{id: 2, attempts: 99},
%User{id: 3, attempts: 2}
]
Thanks for helping!
Upvotes: 6
Views: 1438
Reputation: 121010
Arguably, most elixirish approach would be to use Access
behaviour and Kernel.put_in/3
:
put_in users,
[Access.filter(&match?(%User{id: 2}, &1)),
Access.key(:attempts)],
99
#⇒ [
# %User{attempts: 5, id: 1},
# %User{attempts: 99, id: 2},
# %User{attempts: 2, id: 3}
#]
This might be easily extended to update multiple elements by changing the filter function.
Upvotes: 5
Reputation: 2874
Enum.map(users, fn
%User{id: 2} = user -> %User{user | attempts: 99}
user -> user
end)
You can encapsulate it into some module and give it nice name :)
Upvotes: 6
Reputation: 23137
If you are doing many updates, it's probably best to build a map, so that you don't have to iterate the list for each update.
[
%User{attempts: 5, id: 1},
%User{attempts: 12, id: 2},
%User{attempts: 2, id: 3}
]
|> Map.new(fn user -> {user.id, user} end)
|> Map.update!(2, &%User{&1 | attempts: 99})
|> Map.values()
You can do as many Map.update!/3
calls as you need, to update different ids.
Upvotes: 1