Vinicius Brasil

August 25, 2021

Pattern matching maps and structs in Elixir

Pattern matching in Elixir can be useful to not only match values, but destructuring data. This is useful in several cases against maps and structs (also maps 🤪), for example:

1. Getting the struct type

iex> %struct{} = URI.parse("google.com")
iex> struct
URI

2. Matching the struct type dynamically

iex> type = URI
iex> %^type{} = %URI{}
%URI{}

iex> %^type{} = %User{}
** (MatchError) no match of right hand side value: %User{}

3. Ensuring an object is a struct

iex> %_{} = %URI{}
%URI{}

iex> %_{} = %{}
** (MatchError) no match of right hand side value: %{}

4. Ensuring the map has specific keys

iex> map = %{first_name: "Vinicius", last_name: "Brasil", city: "Curitiba"}
iex> %{first_name: _, last_name: _} = map
%{first_name: "Vinicius", last_name: "Brasil", city: "Curitiba"}

iex> %{age: _} = map
** (MatchError) no match of right hand side value: %{city: "Curitiba", first_name: "Vinicius", last_name: "Brasil"}

5. Ensuring a map is not empty

This one is tricky because %{} matches against any map value, empty or not. For instance:

iex> empty_map = %{}
iex> filled_map = %{age: 21}

iex> %{} = empty_map
%{}

iex> %{} = filled_map
%{age: 21}

To ensure a map actually has keys in a guard clause, you can use Kernel.map_size/1 as the example below:

iex> defmodule Empty do
...>   def is_nil_or_empty?(nil), do: true
...>   def is_nil_or_empty?(map) when map_size(map) == 0, do: true
...>   def is_nil_or_empty?(%{}), do: false
...> end

iex> Empty.is_nil_or_empty?(nil)
true

iex> Empty.is_nil_or_empty?(%{})
true

iex> Empty.is_nil_or_empty?(%{a: 1})
false

About Vinicius Brasil

Building cool stuff with Elixir, OTP and Ruby. Majoring in Theology and musician.