🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of shmibs

shmibs's solution

to Phone Number in the Elixir Track

Published at Jul 13 2018 · 0 comments
Test suite


This solution was written on an old version of Exercism. The tests below might not correspond to the solution code, and the exercise may have changed since this code was written.

Clean up user-entered phone numbers so that they can be sent SMS messages.

The North American Numbering Plan (NANP) is a telephone numbering system used by many countries in North America like the United States, Canada or Bermuda. All NANP-countries share the same international country code: 1.

NANP numbers are ten-digit numbers consisting of a three-digit Numbering Plan Area code, commonly known as area code, followed by a seven-digit local number. The first three digits of the local number represent the exchange code, followed by the unique four-digit number which is the subscriber number.

The format is usually represented as


where N is any digit from 2 through 9 and X is any digit from 0 through 9.

Your task is to clean up differently formatted telephone numbers by removing punctuation and the country code (1) if present.

For example, the inputs

  • +1 (613)-995-0253
  • 613-995-0253
  • 1 613 995 0253
  • 613.995.0253

should all produce the output


Note: As this exercise only deals with telephone numbers used in NANP-countries, only 1 is considered a valid country code.

Running tests

Execute the tests with:

$ elixir phone_number_test.exs

Pending tests

In the test suites, all but the first test have been skipped.

Once you get a test passing, you can unskip the next one by commenting out the relevant @tag :pending with a # symbol.

For example:

# @tag :pending
test "shouting" do
  assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"

Or, you can enable all the tests by commenting out the ExUnit.configure line in the test suite.

# ExUnit.configure exclude: :pending, trace: true

For more detailed information about the Elixir track, please see the help page.


Event Manager by JumpstartLab http://tutorials.jumpstartlab.com/projects/eventmanager.html

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.


if !System.get_env("EXERCISM_TEST_EXAMPLES") do
  Code.load_file("phone_number.exs", __DIR__)

ExUnit.configure(exclude: :pending, trace: true)

defmodule PhoneTest do
  use ExUnit.Case

  test "cleans number" do
    assert Phone.number("(212) 555-0100") == "2125550100"

  @tag :pending
  test "cleans number with dots" do
    assert Phone.number("212.555.0100") == "2125550100"

  @tag :pending
  test "valid when 11 digits and first is 1" do
    assert Phone.number("12125550100") == "2125550100"

  @tag :pending
  test "valid when 11 digits and some decorations" do
    assert Phone.number("+1 (212) 555-0100") == "2125550100"

  @tag :pending
  test "invalid when country calling code is not 1" do
    assert Phone.number("22125550100") == "0000000000"

  @tag :pending
  test "invalid when 9 digits" do
    assert Phone.number("212555010") == "0000000000"

  @tag :pending
  test "invalid when proper number of digits but letters mixed in" do
    assert Phone.number("2a1a2a5a5a5a0a1a0a0a") == "0000000000"

  @tag :pending
  test "invalid with correct number of characters but some are letters" do
    assert Phone.number("2a1a2a5a5a") == "0000000000"

  @tag :pending
  test "invalid when area code begins with 1" do
    assert Phone.number("1125550100") == "0000000000"

  @tag :pending
  test "invalid when area code begins with 0" do
    assert Phone.number("0125550100") == "0000000000"

  @tag :pending
  test "invalid when exchange code begins with 1" do
    assert Phone.number("2121550100") == "0000000000"

  @tag :pending
  test "invalid when exchange code begins with 0" do
    assert Phone.number("2120550100") == "0000000000"

  @tag :pending
  test "area code" do
    assert Phone.area_code("2125550100") == "212"

  @tag :pending
  test "area code with full US phone number" do
    assert Phone.area_code("12125550100") == "212"

  @tag :pending
  test "invalid area code" do
    assert Phone.area_code("(100) 555-1234") == "000"

  @tag :pending
  test "no area code" do
    assert Phone.area_code("867.5309") == "000"

  @tag :pending
  test "pretty print" do
    assert Phone.pretty("2125550100") == "(212) 555-0100"

  @tag :pending
  test "pretty print with full US phone number" do
    assert Phone.pretty("+1 (303) 555-1212") == "(303) 555-1212"

  @tag :pending
  test "pretty print invalid US phone number" do
    assert Phone.pretty("212-155-0100") == "(000) 000-0000"

  @tag :pending
  test "pretty print invalid, short US phone number" do
    assert Phone.pretty("867.5309") == "(000) 000-0000"
defmodule Phone do
  @invalid "0000000000"

  @doc """
  Remove formatting from a phone number.

  Returns "0000000000" if phone number is not valid
  (10 digits or "1" followed by 10 digits)

  ## Examples

  iex> Phone.number("123-456-7890")

  iex> Phone.number("+1 (303) 555-1212")

  iex> Phone.number("867.5309")
  @spec number(String.t) :: String.t
  def number(raw) do
    trim = String.replace raw, ~r/[^[:alnum:]]/, ""
    trimlen = String.length trim
    cond do
      trimlen == 11 && String.first(trim) == "1" ->
        String.slice trim, 1..trimlen
      trimlen == 10 -> trim
      true -> @invalid

  @doc """
  Extract the area code from a phone number

  Returns the first three digits from a phone number,
  ignoring long distance indicator

  ## Examples

  iex> Phone.area_code("123-456-7890")

  iex> Phone.area_code("+1 (303) 555-1212")

  iex> Phone.area_code("867.5309")
  @spec area_code(String.t) :: String.t
  def area_code(raw) do
    String.slice number(raw), 0..2

  @doc """
  Pretty print a phone number

  Wraps the area code in parentheses and separates
  exchange and subscriber number with a dash.

  ## Examples

  iex> Phone.pretty("123-456-7890")
  "(123) 456-7890"

  iex> Phone.pretty("+1 (303) 555-1212")
  "(303) 555-1212"

  iex> Phone.pretty("867.5309")
  "(000) 000-0000"
  @spec pretty(String.t) :: String.t
  def pretty(raw) do
    clean = number(raw)
    "(" <> String.slice(clean, 0..2) <> ") "
    <> String.slice(clean, 3..5) <> "-"
    <> String.slice(clean, 6..9)

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?