I can't seem to find a way to pattern match on a map's key in a function head. Is there a way to do this? What I'm trying to do is run different code depending on whether a certain key already exists in a map or not (and also wanted to avoid if/else and the like)
This is what my code looks like
def my_func(key, %{key => _} = map), do: ...
which gives me this error
** (CompileError) illegal use of variable key inside map key match, maps can only match on existing variable by using ^key
Of course I also tried it using the ^
def my_func(key, %{^key => _} = map), do: ...
which then gives
** (CompileError) unbound variable ^key
I'm using elixir 1.3.1/erlang 19.0 x64 on a windows 8.1 machine. Thanks for reading!
Pattern matching allows developers to easily destructure data types such as tuples and lists. As we will see in the following chapters, it is one of the foundations of recursion in Elixir and applies to other types as well, like maps and binaries.
Pattern matching is the act of checking one or more inputs against a pre-defined pattern and seeing if they match. In Elm, there's only a fixed set of patterns we can match against, so pattern matching has limited application.
12.3) Maps are the "go to" key-value data structure in Elixir. Key-value pairs in a map do not follow any order (that's why the printed map in the example above has a different order than the map that was created).
Pattern matching in computer science is the checking and locating of specific sequences of data of some pattern among raw data or a sequence of tokens. Unlike pattern recognition, the match has to be exact in the case of pattern matching.
You can try several approaches depending on the specific problem and the Elixir version you're using:
If you can just pattern match with the key you need:
defmodule Test do
def my_func(%{"a" => value}), do: {:a, value}
def my_func(%{"b" => value}), do: {:b, value}
def my_func(_), do: :error
end
Then in IEx:
iex(1)> Test.my_func(%{"a" => 1})
{:a, 1}
iex(2)> Test.my_func(%{"b" => 2})
{:b, 2}
Also order of the clauses are important e.g. if you are trying to match %{"b" => 2}
but you have the following map %{"a" => 1, "b" => 2}
, the key "a"
will match first, because is in the first clause:
iex(3)> Test.my_func(%{"a" => 1, "b" => 2})
{:a, 1}
If you want to generate something for every key you can match, I recommend a different approach. For example, if you want to map a function to those keys:
defmodule Test0 do
def my_op({"times_2", value}), do: {"times_2", value * 2}
def my_op({"times_3", value}), do: {"times_3", value * 3}
def my_op({key, value}), do: {key, value}
def my_func(m) do
Enum.map(m, &my_op/1) |> Enum.into(%{})
end
end
So you would get the following:
iex(1)> Test0.my_func(%{"times_2" => 2, "times_3" => 3, "whatever" => 42})
%{"times_2" => 4, "times_3" => 9, "whatever" => 42}
You cannot pattern match a key to a variable. The problem is the compiler needs to generate code to search for something it doesn't know yet. When you pattern match you usually give the compiler some hints of what it'll receive. For keys in maps the hint is the key itself. In this case, even pointing that the first argument should be the key to look for, it is not enough for the compiler. So your approach should be to use an if statement:
defmodule Test1 do
def my_func(k, m) do
if Map.has_key?(k, m) do
... do something when key is found ...
else
... do something when key is not found ...
end
end
end
Elixir 1.10 adds the guard is_map_key/2
that can be used to solve this problem finally!
defmodule Test2 do
def my_func(key, map) when is_map_key(map, key) do
... key is found ...
end
def my_func(_, _) do
... key is not found ...
end
end
This will definitely solve the compilation issue :)
I hope this answers your question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With