在Elixir中,我可以通过调用Kernel.is_map/1
来检查变量是map
还是struct
,这有意义,因为Structs are Maps underneath,但我想区分两者之间。我知道我可以在Struct上调用__struct__
来获取它的模块名称但是在普通地图上调用它会抛出:
** (KeyError) key :__struct__ not found in: %{}
所以我的问题是,如何检查变量是地图还是结构?
示例用例:
# I want to handle struct and map inputs differently in my Module
defmodule DifferentThings do
def do_something(arg) when is_map(arg) do
# Do something with Maps
end
def do_something(arg) when is_struct(arg) do
# But handle Structs differently
# Issue is, `is_struct` does not exist
end
end
答案 0 :(得分:20)
一般来说,检查地图是否为结构:
Map.has_key?(struct, :__struct__)
对于不同的方法声明(更一般的方法是第二个):
defmodule DifferentThings do
def do_something(%{__struct__: _} = arg) do
# ...
end
def do_something(arg) when is_map(arg) do
# ...
end
end
答案 1 :(得分:7)
可以在结构和地图之间进行模式匹配,如下所示
defmodule DifferentThings do
def do_something(arg = %_x{}) do
IO.puts "This is a struct"
end
def do_something(arg = %{}) do
IO.puts "This is a map"
end
end
答案 2 :(得分:4)
对于具有防护的Map vs Struct,您不能使用单独的函数头,但您可以使用模式匹配来实现。
defmodule Guard do
def foo(%{:__struct__ => x }) do
Struct
end
def foo(x) when is_map x do
Map
end
end
答案 3 :(得分:3)
您可以使用keys
轻松查看当前地图的Map.keys/1
。
对于map和struct is_map/1
都将返回true,但在您的示例中:
Map.keys(%{}) will return []
和
Map.keys(struct)
将返回键的集合,例如。 [:__struct__, :name, :age]
。
所以你可以简单地使用:
:__struct__ in Map.keys(struct).
如果您希望将此is_struct
设为宏,那么。
答案 4 :(得分:1)
检查术语是否为map
的最简单方法是使用:
Map.has_key?(map, :__struct__)
它不能起到保护作用,因此我们以前不得不求助于function子句中的:__struct__
键以进行模式匹配:
def some_fun(%__struct__: _module}), do: ...
但这看起来很脏。
is_struct/1
从Erlang / OTP 21开始,引入了一些新的防护措施,我们可以使用它们定义自己的防护措施:
defguard is_struct(term) when is_map(term) and :erlang.is_map_key(:__struct__, term)
defguard is_struct(term, module) when is_struct(term) and :erlang.map_get(:__struct__, term) == module
现在您可以将它们用作功能守卫:
def do_something(account) when is_struct(account, User) do
# Do something with %User{} structs
end
def do_something(struct) when is_struct(struct) do
# Do something with random structs
end
def do_something(map) when is_map(map) do
# Do something with maps
end
注意:这些将很快添加到Elixir标准库本身中。请遵循此ElixirForum thread中的讨论。
答案 5 :(得分:1)
正如Sheharyar所述,Elixir 1.10.0引入了is_struct/1
保护。因此,您的原始代码将可用。