2016-09-28 38 views
3

在Elixir我可以檢查一個變量是map還是struct,通過調用Kernel.is_map/1這是有道理的,因爲Structs are Maps underneath,但我想區分這兩者。我知道我可以調用__struct__在一個結構來獲得它的模塊名稱,但稱這是一個正常的地圖上拋出:如何檢查Map是否也是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 

回答

11

一般檢查,如果地圖是一個結構:

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 
+0

這不會在後臺工作 –

+1

如果你需要模式匹配,可以使用'%{__ struct__:_} = struct' –

2

你可以用Map.keys/1檢查容易keys當前地圖。

對於這兩個地圖和結構is_map/1將返回true,但在你的例子:

Map.keys(%{}) will return [] 

Map.keys(struct) 

將返回鍵的集合,例如。 [:__struct__, :name, :age]

所以,你可以簡單地使用:

:__struct__ in Map.keys(struct). 

如果你想擁有這個is_struct讓它然後宏。

+0

這是完美的!謝謝! – Sheharyar

+2

'Map.has_key?(struct,:__ struct __)' –

+1

@OleksandrAvoyants - 好多了。請張貼它作爲答案。 – Sheharyar

3

你不能有地圖單獨的函數頭帶有警衛的結構,但你可以通過模式匹配來完成。

defmodule Guard do 

    def foo(%{:__struct__ => x }) do 
    Struct 
    end 

    def foo(x) when is_map x do 
    Map 
    end 

end 
5

是可能的模式匹配結構之間的映射,並像下面

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 
+0

非常喜歡'arg =%_x {}',但是你也可以'arg =% _ {}'。 – mljrg