【问题标题】:How to use a variable to refer to a key of a struct?如何使用变量来引用结构的键?
【发布时间】:2020-01-13 13:42:39
【问题描述】:

如何使用变量访问结构的字段:

  var1 = "key1"
  struct1 = %MyStruct{key1: "fdsfd", key2: 33}

  val1 = struct1[:????] # how to use var1 for "key1"?

【问题讨论】:

    标签: elixir


    【解决方案1】:

    使用String.to_existing_atom/1Map.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

    【讨论】:

    • 是否需要将var1转换为原子?
    • 您可以遍历结构的所有字段,将键转换为字符串,然后比较它们,但速度会慢得多。比如:struct1 |> Map.from_struct |> Enum.find(fn {key, _} -> Atom.to_string(key) == var1 end).
    • 但原子不是字符串结构同义词的键吗?
    • 原子和字符串是不同的。一个结构只能有原子键。地图可以将任何术语作为键,因此您可以拥有地图%{:a => 1, "a" => 2}。对于 Elixir,:a"a" 看起来一点也不像。
    【解决方案2】:

    除了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))。不过,我建议不要这样做。它会掩盖您的实际数据,并可能导致您认为您在不同的上下文中拥有字符串键。
    【解决方案3】:

    如果您使用的是 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/2Access.key! 的好处是要么工作要么养大KeyError

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-13
      • 2015-10-19
      相关资源
      最近更新 更多