Avatar of davearonson

davearonson's solution

to Phone Number in the Elixir Track

Published at Jul 13 2018 · 0 comments
Instructions
Test suite
Solution

Note:

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

(NXX)-NXX-XXXX

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

6139950253

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!"
end

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.

Source

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.

phone_number_test.exs

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

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

defmodule PhoneTest do
  use ExUnit.Case

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  @tag :pending
  test "pretty print invalid, short US phone number" do
    assert Phone.pretty("867.5309") == "(000) 000-0000"
  end
end
defmodule Phone do
  @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")
  "1234567890"

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

  iex> Phone.number("867.5309")
  "0000000000"
  """
  @spec number(String.t) :: String.t
  def number(raw) do
    validate_length(do_number(String.graphemes(raw)))
  end

  @bad_number "0000000000"

  defp do_number([]), do: ""

  defp do_number([first|rest]), do: "#{digit_value(first)}#{do_number(rest)}"

  # too bad ranges are only for integers :-P
  defp digit_value(char) when char >= "0" and char <= "9", do: char
  defp digit_value(char) when char >= "a" and char <= "z", do: "0"
  defp digit_value(_), do: ""

  defp validate_length(str), do: do_validate_length(str, String.length(str))

  defp do_validate_length(str, 10), do: str

  defp do_validate_length(str, 11) do
    # String.first is not allowed in guards, and ["1"|rest] is not valid :-P
    case String.first(str) do
      "1" -> String.slice(str, 1, 10)
      _   -> @bad_number
    end
  end

  defp do_validate_length(_, _), do: @bad_number


  @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")
  "123"

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

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


  @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
    do_pretty(number(raw))
  end

  defp do_pretty(num) do
    "(#{String.slice(num, 0..2)}) #{String.slice(num, 3..5)}-#{String.slice(num, 6..9)}"
  end

end

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?