Yarn rolls Photo by Alex Block on Unsplash

Using string pattern matching to format text? Sure!

We store text like phone numbers, and Social Security Numbers (SSN) as unformatted text. When a user submits the data, we strip all formatting and non-number characters from it. If a user enters 800.555.4444, we store 8005554444. When it comes time to render the value into a PDF, document or export it in some other way, we format it in a consistent way like (800) 555-4444.

Elixir (and the BEAM) supports pattern matching binary strings, making this very clean and fun.

This is documented in Kernel.SpecialForms under «arg». You can use this in very powerful ways like matching on web protocols, parsing PNGs and more. However, sometimes the straight-forward “string” uses are harder to find documented. I’m hoping to add to that here.

The power is in the binary pattern match where I specify the length of each segment and bind it to a variable.

defmodule Utils.Format do
  def phone(<< three::binary-size(3), four::binary-size(4) >>) do
    "#{three}-#{four}"
  end
end

This example only matches a 7-character string.

Here’s the full version that also supports formatting a 10-character value where an area-code is included.

defmodule Utils.Format do
  @moduledoc """
  Functions for common string formatting.
  """

  @doc """
  Format a phone number with an expected area code.

  ## Examples

      iex> Format.phone("8005554444")
      "(800) 555-4444"

      iex> Format.phone("5554444")
      "555-4444"

      iex> Format.phone("short")
      "short"

      iex> Format.phone(nil)
      nil

  """
  @spec phone(value :: nil | String.t()) :: nil | String.t()
  def phone(value)
  def phone(nil), do: nil
  def phone(<< area::binary-size(3), three::binary-size(3), four::binary-size(4) >>) do
    "(#{area}) #{three}-#{four}"
  end
  def phone(<< three::binary-size(3), four::binary-size(4) >>) do
    "#{three}-#{four}"
  end
  def phone(other) when is_binary(other), do: other
end

Why not make this a library? I find that usually my application is pretty focused in what it needs. A public library needs to support many different formats and different use-cases to be valuable.

Elixir makes it so easy to do just what I need for my application that I don’t need to bring in a library or worry about creating one.

I love Elixir. I solved a need in my application without a single if conditional. It’s fully declarative, clear and performant.

Where can you use binary pattern matching?