How to use a variable to refer to a key of a struct?
If you're using Elixir 1.3 or later, you can use the Access.key!
with get_in
on any struct:
var1 = "key1"
struct1 = %MyStruct{key1: "fdsfd", key2: 33}
val1 = get_in struct1, [Access.key!(String.to_existing_atom(var1))]
Prior to Elixir 1.3, it wasn't possible to use get_in/3
(where the lookup path is variable), and you could only use get_in/2
like val1 = get_in struct1.var1
. Access.key!
has the benefit of either working or raising KeyError
.
Besides Map.get/2
, you can pattern match to get the value, or implement the Access behaviour on your struct, so you can use struct1[var1]
as you tried.
(Turn your var1 = "key1"
into an atom with String.to_existing_atom/1
as suggested.)
Given a struct1 = %MyStruct{key1: "fdsfd", key2: 33}
Pattern matching:
iex> %{^var1 => value} = struct1
iex> value
"fdsfd"
Access behaviour:
defmodule MyStruct do
defstruct key1: nil, key2: nil
def fetch(my_struct, key) do
{:ok, Map.get(my_struct, key)}
end
end
iex> my_struct[var1]
"fdsfd"
Note that I didn't implement the full Access behaviour. See the docs on Access callbacks for more.
Use String.to_existing_atom/1
and Map.get/2
(as structs are actually maps):
iex(1)> defmodule MyStruct do
...(1)> defstruct [:key1, :key2]
...(1)> end
iex(2)> var1 = "key1"
"key1"
iex(3)> struct1 = %MyStruct{key1: "fdsfd", key2: 33}
%MyStruct{key1: "fdsfd", key2: 33}
iex(4)> val1 = Map.get(struct1, String.to_existing_atom(var1))
"fdsfd"
The [:key]
syntax will not work with structs by default as it uses the Access
protocol which has to be implemented by the user for each struct.
String.to_existing_atom/1
will throw an error if the atom does not already exist but is safer to use than converting arbitrary input to atom and it will definitely exist if you have a struct with that key already defined. See this question for more details.