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.

Tags:

Elixir