【发布时间】:2020-01-13 13:42:39
【问题描述】:
如何使用变量访问结构的字段:
var1 = "key1"
struct1 = %MyStruct{key1: "fdsfd", key2: 33}
val1 = struct1[:????] # how to use var1 for "key1"?
【问题讨论】:
标签: elixir
如何使用变量访问结构的字段:
var1 = "key1"
struct1 = %MyStruct{key1: "fdsfd", key2: 33}
val1 = struct1[:????] # how to use var1 for "key1"?
【问题讨论】:
标签: elixir
使用String.to_existing_atom/1 和Map.get/2(因为结构实际上是映射):
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"
[:key] 语法默认不适用于结构,因为它使用 Access 协议,该协议必须由用户为每个结构实现。
String.to_existing_atom/1 如果 atom 不存在但使用起来比将任意输入转换为 atom 更安全,则会抛出错误,如果您有一个已经定义了该键的结构,它肯定会存在。详情请见this question。
【讨论】:
struct1 |> Map.from_struct |> Enum.find(fn {key, _} -> Atom.to_string(key) == var1 end).
%{:a => 1, "a" => 2}。对于 Elixir,:a 和 "a" 看起来一点也不像。
除了Map.get/2,您还可以通过模式匹配来获取值,或者在您的结构上实现访问行为,因此您可以尝试使用struct1[var1]。
(按照建议将您的var1 = "key1" 变成带有String.to_existing_atom/1 的原子。)
给定一个struct1 = %MyStruct{key1: "fdsfd", key2: 33}
模式匹配:
iex> %{^var1 => value} = struct1
iex> value
"fdsfd"
访问行为:
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"
请注意,我没有实现完整的访问行为。请参阅the docs on Access callbacks 了解更多信息。
【讨论】:
fetch(my_struct, key) when is_binary(key), do: fetch(my_struct, String.to_existing_atom(key))。不过,我建议不要这样做。它会掩盖您的实际数据,并可能导致您认为您在不同的上下文中拥有字符串键。
如果您使用的是 Elixir 1.3 或更高版本,您可以在任何结构上使用 Access.key! 和 get_in:
var1 = "key1"
struct1 = %MyStruct{key1: "fdsfd", key2: 33}
val1 = get_in struct1, [Access.key!(String.to_existing_atom(var1))]
在 Elixir 1.3 之前,无法使用 get_in/3(其中查找路径是可变的),您只能像 val1 = get_in struct1.var1 一样使用 get_in/2。 Access.key! 的好处是要么工作要么养大KeyError。
【讨论】: