与地图内的地图进行模式匹配(Elixir)

Pattern matching with a map inside a map (Elixir)

我在 Elixir 应用程序中使用以下功能:

  def emojify(%Conv{status: 200, resp_headers: resp_headers} = conv) do
    body = if resp_headers["Content-Type"] == "application/json", do: conv.resp_body, else: "" <> "\n\n" <> conv.resp_body <> "\n\n" <> ""
  
    %{ conv | resp_body: body }
  end

  def emojify(%Conv{} = conv), do: conv

我想重构它以消除对其中 if 的需要。我想这样做的方法是通过模式匹配 resp_headers 内的内容类型,但那将是地图内地图的模式匹配(更准确地说是地图内的结构,但我不知道不认为这个细节在这种情况下是相关的),我不知道该怎么做。

我正在尝试类似 def emojify(%Conv{status: 200, resp_headers: %{"Content-Type" => "application/json"}} = conv) 的方法,但我知道这只有在 resp_headers 中只有 "Content-Type" => "application/json" 的情况下才有效,但事实并非如此。那么我怎样才能做到这一点?

I understand that this would only work if resp_headers has only "Content-Type" => "application/json" inside it

这个假设是错误的。如果匹配中的字段出现在输入 中,则地图匹配,所有固定变量都匹配。你没有固定变量,所以你没问题。

经常使用匹配到空映射而不是 is_map(map) 保护作为 foo(%{} = map) do ...

%{} = %{foo: 42, bar: :baz} # success

# and also
%{foo: foo} = %{foo: 42, bar: :baz} # success
foo
#⇒ 42

也就是说,答案是

def emojify(%Conv{
    status: 200,
    resp_headers: %{"Content-Type" => "application/json"}} = conv),
  do: %{ conv | resp_body: conv.resp_body }

def emojify(%Conv{status: 200} = conv),
  do: %{ conv | resp_body: "" <> "\n\n" <> conv.resp_body <> "\n\n" <> ""}

def emojify(%Conv{} = conv), do: conv

旁注: 带有固定变量的示例。

%{foo: 42} = %{foo: 42, bar: :baz} # success

foo = 42
%{foo: ^foo} = %{foo: 42, bar: :baz} # success

%{foo: ^foo} = %{foo: :incorrect, bar: :baz} # raises

你可以像这样进行模式匹配

def emojify( %{status: 200, resp_headers: %{"Content-Type" => "application/json" } } = conv
) do
    %{ conv | resp_body: conv.resp_body }    
   end
   def emojify(%{status: 200} = conv) do
    %{ conv | resp_body: "" <> "\n\n" <> conv.resp_body <> "\n\n" <> "" }
   end
   def emojify(conv), do: conv

您始终可以对映射或结构中特定键的值进行模式匹配

有关更多信息,请查看 joyofelixir