Matching a map exactly

471 views
Skip to first unread message

Scott Thompson

unread,
Mar 27, 2016, 12:33:07 AM3/27/16
to elixir-lang-talk
In writing a function with pattern matching I know I can match a map argument by providing one or more fields:

def my_function(%{direction: some_var})
  # ...do some stuff with some_var ...
end


This function should match maps with extraneous keys like

%{ ignored: "fred", direction: :north}

However, is there a way to mark a matching pattern so that the Map has to match it EXACTLY?  So that the match will not be taken if the Map contains any keys other than the ones specified?

Bruno Luis Panuto Silva

unread,
Mar 27, 2016, 1:41:42 AM3/27/16
to elixir-lang-talk
Short answer is no. Not in a map.
But on a struct, however, you ensure that you can't put in a field that wasn't defined before.
If you really need maps, you could assert that it matches using a combination of the Map.keys function, like this:

defmodule ExactMap do

   
def match(map, keys), do: Map.keys(map) == keys
end

And use it, like:
iex> ExactMap.match(%{ignored: "fred", direction: :north}, [:direction])

Peter Hamilton

unread,
Mar 27, 2016, 2:05:20 AM3/27/16
to elixir-lang-talk

You can match on all keys and include a guard on map_size.

Bruno's answer about structs isn't quite accurate. There's no protection against structs with extra fields added via Map.put/3. If you always update via the map update syntax %{struct | :key => "value"} then you should be fine, but there nothing but convention protecting you from messing with the strict internals by treating it as a map.


--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/0b8f5ec3-9965-4527-966a-beeeb219fb86%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Scott Thompson

unread,
Mar 28, 2016, 2:40:22 PM3/28/16
to elixir-lang-talk
This worked perfectly.  Thank you very much.

Peter Hamilton

unread,
Apr 9, 2016, 12:05:11 PM4/9/16
to elixir-lang-talk
I found another way to do it, thought I'd share here.

iex(1)> z = %{a: 1, b: 2}
%{a: 1, b: 2}
iex(2)> case %{a: 1, b: 2} do
...(2)>   ^z -> true
...(2)>   _ -> false
...(2)> end
true
iex(3)> case %{a: 1, b: 2, c: 3} do
...(3)>   ^z -> true
...(3)>   _ -> false
...(3)> end
false
iex(4)> case %{a: 1, b: 2, c: 3} do
...(4)>   %{a: 1, b: 2} -> true
...(4)>   _ -> false
...(4)> end
true

This won't work in def function heads, but it will work anywhere you can use pinned vars.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages