Exercism - Phone Number
This post shows you how to get Phone Number exercise of Exercism.
Preparation
Before we click on our next exercise, let’s see what concepts of DART we need to consider

So we need to use the following concepts.
FormatException
FormatException is thrown when a string or other data does not have the expected format. It’s useful for validation errors.
void main() {
void validate(String input) {
if (input.contains('a')) {
throw FormatException('letters not permitted');
}
if (input.length < 10) {
throw FormatException('must not be fewer than 10 digits');
}
}
try {
validate('abc123');
} catch (e) {
print(e); // FormatException: letters not permitted
}
}
String Contains with RegExp
The contains() method can check if a string contains a pattern matching a regular expression. It’s useful for detecting invalid characters.
void main() {
String number = "1-800-CALL-NOW";
// Check for letters
if (number.contains(RegExp(r'[a-zA-Z]'))) {
print('Contains letters');
}
// Check for digits
if (number.contains(RegExp(r'[0-9]'))) {
print('Contains digits');
}
// Pattern breakdown:
// [a-zA-Z] - character class matching any letter (lowercase or uppercase)
}
String ReplaceAll with RegExp
The replaceAll() method can use regular expressions to remove or replace patterns. Character classes and negated character classes are useful for this.
void main() {
String number = "+1 (613)-995-0253";
// Remove all non-digits
String digits = number.replaceAll(RegExp(r'[^0-9]'), '');
print(digits); // "16139950253"
// Pattern breakdown:
// [^0-9] - negated character class: anything that is NOT a digit
// This removes all non-digit characters
// Remove specific characters
String cleaned = number
.replaceAll(RegExp(r'[0-9]'), '') // Remove digits
.replaceAll(RegExp(r'\s'), '') // Remove whitespace
.replaceAll('+', '') // Remove plus
.replaceAll('-', ''); // Remove dash
print(cleaned); // "( )-."
}
String isEmpty and isNotEmpty
The isEmpty property returns true if the string has no characters, and isNotEmpty returns true if it has at least one character.
void main() {
String empty = '';
String notEmpty = 'abc';
// Check if empty
print(empty.isEmpty); // true
print(notEmpty.isEmpty); // false
// Check if not empty
print(empty.isNotEmpty); // false
print(notEmpty.isNotEmpty); // true
// Use for validation
String cleaned = "abc";
if (cleaned.isNotEmpty) {
throw FormatException('Invalid characters found');
}
}
String Indexing
You can access individual characters in a string using square brackets with an index. The first character is at index 0.
void main() {
String number = "6139950253";
// Access by index
print(number[0]); // '6' (first digit - area code start)
print(number[3]); // '9' (fourth digit - exchange code start)
// Validate specific positions
if (number[0] == '0') {
print('Area code cannot start with zero');
}
if (number[3] == '1') {
print('Exchange code cannot start with one');
}
}
String Substring Method
The substring() method extracts a portion of a string. It’s useful for removing prefixes like country codes.
void main() {
String number = "16139950253";
// Get substring from index 1 to end
String withoutCountry = number.substring(1);
print(withoutCountry); // "6139950253"
// Get substring with start and end
String areaCode = number.substring(1, 4);
print(areaCode); // "613"
// Remove country code if present
if (number.length == 11 && number[0] == '1') {
number = number.substring(1);
}
}
Conditional Logic
Conditional statements allow you to execute different code based on conditions. This is essential for validation and error handling.
void main() {
String digits = "6139950253";
// Validate length
if (digits.length < 10) {
throw FormatException('too short');
}
if (digits.length > 11) {
throw FormatException('too long');
}
// Validate specific positions
if (digits[0] == '0') {
throw FormatException('area code cannot start with zero');
}
if (digits[0] == '1') {
throw FormatException('area code cannot start with one');
}
}
Regular Expressions
Regular expressions allow you to match patterns in strings. Character classes [] match any character within, and ^ at the start negates the class.
void main() {
// Character class: matches any digit
RegExp digits = RegExp(r'[0-9]');
// Negated character class: matches anything NOT a digit
RegExp nonDigits = RegExp(r'[^0-9]');
// Character class: matches any letter
RegExp letters = RegExp(r'[a-zA-Z]');
// Whitespace
RegExp whitespace = RegExp(r'\s');
// Use in replaceAll
String number = "+1 (613)-995-0253";
String digitsOnly = number.replaceAll(RegExp(r'[^0-9]'), '');
print(digitsOnly); // "16139950253"
}
Introduction
You’ve joined LinkLine, a leading communications company working to ensure reliable connections for everyone. The team faces a big challenge: users submit phone numbers in all sorts of formats — dashes, spaces, dots, parentheses, and even prefixes. Some numbers are valid, while others are impossible to use.
Your mission is to turn this chaos into order. You’ll clean up valid numbers, formatting them appropriately for use in the system. At the same time, you’ll identify and filter out any invalid entries.
The success of LinkLine’s operations depends on your ability to separate the useful from the unusable. Are you ready to take on the challenge and keep the connections running smoothly?
Instructions
Clean up 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.
Sometimes they also have the country code (represented as 1 or +1) prefixed.
Your task is to clean up differently formatted telephone numbers by removing punctuation and the country code if present.
Examples
For example, the inputs:
+1 (613)-995-0253613-995-02531 613 995 0253613.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.
What is the North American Numbering Plan?
The North American Numbering Plan (NANP) is a telephone numbering plan that encompasses 25 distinct regions in twenty countries primarily in North America, including the Caribbean and some Atlantic and Pacific islands. The NANP uses a standardized format: a three-digit area code, followed by a three-digit exchange code, and finally a four-digit subscriber number.
— Wikipedia
How can we clean and validate phone numbers?
To clean and validate phone numbers:
- Check for invalid characters: Reject numbers containing letters
- Check for invalid punctuation: Only allow digits, spaces, +, -, ., and parentheses
- Extract digits: Remove all non-digit characters
- Validate length: Must be 10 or 11 digits
- Handle country code: If 11 digits, must start with 1, then remove it
- Validate area code: First digit (after removing country code) must be 2-9
- Validate exchange code: Fourth digit must be 2-9
The key insight is using multiple validation steps to ensure the phone number is properly formatted and follows NANP rules.
For example, with “+1 (613)-995-0253”:
- Check letters: none ✓
- Check punctuation: valid ✓
- Extract digits: “16139950253”
- Length: 11 digits ✓
- Country code: starts with 1 ✓
- Remove country code: “6139950253”
- Area code: starts with 6 (2-9) ✓
- Exchange code: starts with 9 (2-9) ✓
- Result: “6139950253”
Solution
class PhoneNumber {
String clean(String number) {
// Check for letters
if (number.contains(RegExp(r'[a-zA-Z]'))) {
throw FormatException('letters not permitted');
}
// Check for invalid punctuation
// Valid: digits (0-9), whitespace (\s), plus (+), dash (-), dot (.), parentheses ()
// Remove all valid characters and see if anything is left
final withoutValidChars = number
.replaceAll(RegExp(r'[0-9]'), '') // remove digits
.replaceAll(RegExp(r'\s'), '') // remove whitespace
.replaceAll('+', '') // remove plus
.replaceAll('-', '') // remove dash
.replaceAll('.', '') // remove dot
.replaceAll('(', '') // remove open paren
.replaceAll(')', ''); // remove close paren
if (withoutValidChars.isNotEmpty) {
throw FormatException('punctuations not permitted');
}
// Extract only digits
final digits = number.replaceAll(RegExp(r'[^0-9]'), '');
// Validate length
if (digits.length < 10) {
throw FormatException('must not be fewer than 10 digits');
}
if (digits.length > 11) {
throw FormatException('must not be greater than 11 digits');
}
// Handle country code
String cleanNumber = digits;
if (digits.length == 11) {
if (digits[0] != '1') {
throw FormatException('11 digits must start with 1');
}
cleanNumber = digits.substring(1);
}
// Validate area code
if (cleanNumber[0] == '0') {
throw FormatException('area code cannot start with zero');
}
if (cleanNumber[0] == '1') {
throw FormatException('area code cannot start with one');
}
// Validate exchange code
if (cleanNumber[3] == '0') {
throw FormatException('exchange code cannot start with zero');
}
if (cleanNumber[3] == '1') {
throw FormatException('exchange code cannot start with one');
}
return cleanNumber;
}
}
Let’s break down the solution:
-
String clean(String number)- Main method that cleans and validates phone numbers:- Takes a phone number string (may contain formatting)
- Returns cleaned 10-digit number
- Throws
FormatExceptionfor invalid inputs
-
Letter validation:
if (number.contains(RegExp(r'[a-zA-Z]'))): Checks if string contains any lettersthrow FormatException('letters not permitted'): Throws error if letters found- Phone numbers should only contain digits and allowed punctuation
-
Punctuation validation:
withoutValidChars: Removes all valid characters (digits, spaces, +, -, ., parentheses)- Uses multiple
replaceAll()calls to strip valid characters if (withoutValidChars.isNotEmpty): If anything remains, it’s invalid punctuation- Throws error for invalid punctuation characters
-
Extract digits:
final digits = number.replaceAll(RegExp(r'[^0-9]'), ''): Removes all non-digit characters[^0-9]: Negated character class - matches anything that is NOT a digit- Results in a string containing only digits
-
Length validation:
if (digits.length < 10): Must have at least 10 digitsif (digits.length > 11): Must have at most 11 digits- Throws appropriate errors for invalid lengths
-
Country code handling:
if (digits.length == 11): If 11 digits, check for country codeif (digits[0] != '1'): Country code must be 1cleanNumber = digits.substring(1): Remove country code, keep remaining 10 digits
-
Area code validation:
if (cleanNumber[0] == '0'): Area code cannot start with 0if (cleanNumber[0] == '1'): Area code cannot start with 1- Area code must start with 2-9 (N in NXX format)
-
Exchange code validation:
if (cleanNumber[3] == '0'): Exchange code (4th digit) cannot start with 0if (cleanNumber[3] == '1'): Exchange code cannot start with 1- Exchange code must start with 2-9 (N in NXX format)
-
return cleanNumber- Return the cleaned number:- Returns the validated 10-digit phone number
The solution thoroughly validates phone numbers by checking for invalid characters, validating length, handling country codes, and ensuring area and exchange codes follow NANP rules.
You can watch this tutorial on YouTube. So don’t forget to like and subscribe. 😉
Watch on YouTube