I do a fair amount of pattern matching in this style:
def action(%{start_date: start_date, amount: amount, notify: notify %}) do
# some action
end
Most of the times, the name I choose for the parameters are the same name in the map. Is there a shortcut of specifying that pattern matching case without repeating the same name for the key and the value ?
Something in the line of this pseudo code:
def action(%{start_date: %s, amount: %s, notify: %s}) do
IO.inspect(start_date)
# some action
end
Python and Elixir Programming Bundle Course Pattern matching is a technique which Elixir inherits form Erlang. It is a very powerful technique that allows us to extract simpler substructures from complicated data structures like lists, tuples, maps, etc. A match has 2 main parts, a left and a right side.
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).
Update a map But as with all values in Elixir, a map is immutable, so the result of the update is a new map. The simplest way to update a map is with this syntax: new_map = %{ old_map | key => value, ... } . However, this syntax won't add a new key to a map.
AFAIK, there is nothing out of the box, but one could simply create a macro for themselves to serve that purpose:
defmodule M do
defmacro struct(params) do
{:%{}, [], Enum.map(params, fn e -> {e, {e, [], Elixir}} end)}
end
end
defmodule Test do
require M # to use macro
def action(M.struct([:a, :b]) = params),
do: IO.inspect params, label: "Params are"
end
Test.action(%{a: 42, b: :ok})
#⇒ Params are: %{a: 42, b: :ok}
Test.action(%{a: 42})
** (FunctionClauseError) no function clause matching in Test.action/1
The code above is of course just an MCVE, you probably need to enhance it somehow to handle corner cases more gracefully (and, probably, to have the more explicit readable macro that behaves smarter than just spitting the AST out, and considers the binding, etc,) but I believe that explains the idea.
I introduced a sigil, ~m{...}
, to achieve destructing assignment.
~m{foo bar} = %{foo: 1, bar: 2}
foo #=> 1
bar #=> 2
Here's how I implement the sigil
defmodule DestructingAssignment do
defmacro __using__(_) do
quote do: import unquote(__MODULE__)
end
defmacro sigil_m({:<<>>, _line, [string]}, []) do
spec = string
|> String.split
|> Stream.map(&String.to_atom/1)
|> Enum.map(&{&1, {&1, [], nil}})
{:%{}, [], spec}
end
end
Usage
defmodule Foo do
use DestructingAssignment
def foo(~m{bar}) do
# Do whatever you want with bar
IO.inspect(bar)
end
end
Foo.foo(%{bar: 1, baz: 2})
1
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