Get the first user's metas from the Presence Struct

Hello all,

I’m trying to work with the leaves/joins structs that Presence sends over the wire.

When I type, state.leaves, I this:

%{      
  "1" => %{
    metas: [
      %{
        has_control: false,
        input_val: "",
        name: "User 1",
        phx_ref: "X9wa49K5YQ0="
      }
    ]
  }
}

“1” is the user’s id, so I can’t predict what this will be in advance. How do I access the metas from this? In Ruby I would just call state.leaves.first.metas or state.leaves[0].metas. What’s the equivalent for Elixir?

Thanks in advance! Searched all over and couldn’t find how to do it, even converting it to a List and calling List.first…

Edit: Got this to work, but is there a simpler way?

List.first(List.first(Map.values(state.leaves)).metas).has_control

In Elixir Maps aren’t ordered so there is no concept of a first element in a Map.

So which element is “first” when the Map is converted to a list is dependent on the underlying implementation which can change in the future. So retrieving it is only safe if there is only one element in the map.

That being said:

iex(1)> m = %{
...(1)>   "1" => %{
...(1)>     metas: [
...(1)>       %{
...(1)>         has_control: false,
...(1)>         input_val: "",
...(1)>         name: "User 1",
...(1)>         phx_ref: "X9wa49K5YQ0="
...(1)>       }
...(1)>     ]
...(1)>   }
...(1)> }
%{
  "1" => %{
    metas: [
      %{
        has_control: false,
        input_val: "",
        name: "User 1",
        phx_ref: "X9wa49K5YQ0="
      }
    ]
  }
}
iex(2)> [%{metas: [%{has_control: has_control} | _]} | _] = Map.values(m)
[
  %{
    metas: [
      %{
        has_control: false,
        input_val: "",
        name: "User 1",
        phx_ref: "X9wa49K5YQ0="
      }
    ]
  }
]
iex(3)> IO.puts("#{inspect(has_control)}")
false
:ok
iex(4)> 
iex(1)> has_control_from_values = fn
...(1)>   [] ->
...(1)>     nil
...(1)> 
...(1)>   [%{metas: [%{has_control: has_control} | _]} | _] ->
...(1)>     has_control
...(1)> end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(2)> has_control_value = fn m -> m |> Map.values() |> has_control_from_values.() end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(3)> m = %{
...(3)>   "1" => %{
...(3)>     metas: [
...(3)>       %{
...(3)>         has_control: false,
...(3)>         input_val: "",
...(3)>         name: "User 1",
...(3)>         phx_ref: "X9wa49K5YQ0="
...(3)>       }
...(3)>     ]
...(3)>   }
...(3)> }
%{
  "1" => %{
    metas: [
      %{
        has_control: false,
        input_val: "",
        name: "User 1",
        phx_ref: "X9wa49K5YQ0="
      }
    ]
  }
}
iex(4)> IO.puts("#{inspect(has_control_value.(m))}")
false
:ok
iex(5)> IO.puts("#{inspect(has_control_value.(%{}))}")
nil
:ok
iex(6)> 

Now if you are trying to find the “user in control” simply use Map.to_list/1 and use Enum.find/3 on the list.

3 Likes