Pattern matching a map in function parameters

Hello, I am just wondering whether this is possible, or am I doing something wrong.
If so, what would be the best way to pattern match this type of thing, all in one function with a case statement?

def test1(%{num: num}), do: num + 1
def test1(%{num: num, acc: acc}), do: num + acc

iex(8)> test1(%{num: 1})
2
iex(9)> test1(%{num: 1, acc: 5})
2

I expected to receive 6 on the second function call iex(9).

1 Like

The order of the functions is highly important, as the first match wins…

You can switch test1 functions to make it work.

5 Likes

Genius, thank you very much :smiley:

The usual rule of thumb is to go from the more specific, to the more generic…

Of course for each rule You can find exception :slight_smile:

6 Likes

It may also be helpful to clarify that maps, unlike lists, allow for partial matches, so:

%{num: num} = %{num: 1, acc: 5} # Match!
[a, b, c] = [1, 2, 3, 4] # No match!
4 Likes

Another option is to consider 1 as a default value of acc and then you can do it with a single function clause

def test1(%{num: num} = m), do: num + Map.get(m, :acc, 1)
4 Likes

I would say list does partial match too…

[a, b, c | rest] = [1, 2, 3, 4] # match!
3 Likes

Indeed it does, but it’s not an exact analogue to my point. Partial matching a list requires being explicit about it whereas maps just do it. But yes, absolutely worth mentioning for beginners that there is a way to do it with lists!

2 Likes

… and how do I match a map where keys can be either missing or their values set to nil? Querying in both cases returns nil

gin = %{"a" => "1"}
%{"a" => "1"}
iex(10)> gin["b"]
nil

and

iex(11)> gin = %{"a" => "1", "b" => nil}
%{"a" => "1", "b" => nil}
iex(12)> gin["b"]
nil

now, surely I can’t do anything like:

iex(13)> %{"a" => _, "b" => nil, "c" => nil} = gin
** (MatchError) no match of right hand side value: %{"a" => "1", "b" => nil}

but what should one do instead? The goal is to have two functions matched on the content of the argument. Like "if some keys are missing OR their values are nil then function x(). Otherwise function y()"

1 Like

You can’t do that with pattern matching. You have to write the one line function that uses if and Map.get to determine and then call x or y function.

2 Likes

Roger. I hoped that maybe I might still be missing some pattern matching… well… usage patterns.