collectivelysuperior
Accessing list element with put_in for lists of structs
Let’s say there is a list of structs as a field of a parent struct. Due to the nested nature of the list of maps in the struct, I have been using put_in to insert a new value into the child struct. However, I find myself having to get the index of the child struct in the list and then using that index to access the child struct in the nested list. Is there a better way of doing this? I have searched online and haven’t found much useful info.
In my use case (shown below), the parent struct is a game and the list contains player structs. I am trying to update the player in the list to be the opposite of their current mute boolean.
defmodule Game do
defstruct id: nil, name: nil, password: nil, connected_users: [], messages: []
end
defmodule Player do
defstruct id: nil, muted?: false
end
def mute(%{connected_players: connected_players} = game, player) do
index = Enum.find(connected_players, &(player == &1.id)
game
|> put_in([:connected_players, Access.at!(index), :muted?], not player.muted?)
end
Marked As Solved
thiagomajesk
Hi @collectivelysuperior, welcome to the forum!
I think you can do try something like this:
def mute(%{connected_players: connected_players} = game, %{id: player_id}) do
players = Enum.map(connected_players, fn
{id: ^player_id, muted?: false} = player -> %{player | muted?: true}
player -> player
end
%{game | connected_players: players}
Also Liked
qhwa
I think finding a player by id is quite frequent. For example, when a player joins the game, you also want to make sure you won’t add him/her twice if already in the room.
So I often end up with having the connected_players list as a map, whose keys are ids. Would that be easier for you?
%Game{connected_players: %{"foo" => %Player{id: "foo", ...}}}
shd42
If you want to keep using put_in, you could do it like this (haven’t tested it, but should be fine):
def mute(%Game{} = game, %Player{id: id} = player) do
put_in(game, [
Access.key(:connected_players),
Access.filter(&match?(%{id: ^id}, &1)),
Access.key(:muted?)
], not player.muted?)
end







