Phone Decoder (Code Challenge)

  • Page Owner: Not Set
  • Last Reviewed: 2019-09-13

Phone Decoder

Create a program that converts a phone number with letters to one with only numbers.

Number Letter
0      none
1      none
2      ABC
3	   DEF
4	   GHI
5	   JKL
6	   MNO
7	   PQRS
8	   TUV
9	   WXYZ

Examples

textToNum("123-647-EYES") ➞ "123-647-3937"

textToNum("(325)444-TEST") ➞ "(325)444-8378"

textToNum("653-TRY-THIS") ➞ "653-879-8447"

textToNum("435-224-7613") ➞ "435-224-7613"

Notes

All inputs will be formatted as a string representing a proper phone number in the format XXX-XXX-XXXX or (XXX)XXX-XXXX, using numbers and capital letters. Check the Resources tab for more info on telephone keypads.

Tests

textToNum("123-647-EYES") == "123-647-3937"
textToNum("(325)444-TEST") == "(325)444-8378"
textToNum("653-TRY-THIS") == "653-879-8447"
textToNum("435-224-7613") == "435-224-7613"
textToNum("(33D)ONT-FAIL") == "(333)668-3245"
textToNum("(025)445-6741") == "(025)445-6741"

Additional Posts

I forced the formatting because any other way is stupid. Consistency, people.

void Main()
{
	var map = new Dictionary<int, HashSet<Char>>() {
		{ 2, new HashSet<Char>("ABC") },
		{ 3, new HashSet<Char>("DEF") },
		{ 4, new HashSet<Char>("GHI") },
		{ 5, new HashSet<Char>("JKL") },
		{ 6, new HashSet<Char>("MNO") },
		{ 7, new HashSet<Char>("PQRS") },
		{ 8, new HashSet<Char>("TUV") },
		{ 9, new HashSet<Char>("WXYZ") }
	};

	TextToNumber("123-647-EYES", map).Dump();
	TextToNumber("(325)444-TEST", map).Dump();
	TextToNumber("653-TRY-THIS", map).Dump();
	TextToNumber("435-224-7613", map).Dump();
}


public string TextToNumber(string number, Dictionary<int, HashSet<Char>> map)
{
	if(number.Count(c => Char.IsLetterOrDigit(c)) != 10)
	{
		throw new ArgumentException("Number must have exactly 10 valid characters");
	}
	
	var formatString = "{0}{1}{2}-{3}{4}{5}-{6}{7}{8}{9}";
	
	return String.Format(
		formatString,
		number
		 .Where(c => Char.IsLetterOrDigit(c))
	   	 .Select(c => Char.IsLetter(c)
		   ? map.First(i => i.Value.Contains(Char.ToUpper(c))).Key.ToString()
		   : c.ToString())
		 .ToArray()
	);
}

If you don't care about formatting (per the spec), then this is a pretty straight-forward lookup.

fn text_to_num(test:String) -> String {
    test.chars().map(|letter| match letter {
            'A' | 'B' | 'C' => '2',
            'D' | 'E' | 'F' => '3',
            'G' | 'H' | 'I' => '4',
            'J' | 'K' | 'L' => '5',
            'M' | 'N' | 'O'  => '6',
            'P' | 'Q' | 'R' | 'S' => '7',
            'T' | 'U' | 'V' => '8',
            'W' | 'X' | 'Y' | 'Z' => '9',
            other => other
        }).collect::<String>()
}

pub fn execute() {
    println!("{}", text_to_num("123-647-EYES".to_owned()));// ➞ "123-647-3937"
    println!("{}", text_to_num("(325)444-TEST".to_owned()));// ➞ "(325)444-8378"
    println!("{}", text_to_num("653-TRY-THIS".to_owned()));// ➞ "653-879-8447"
    println!("{}", text_to_num("435-224-7613".to_owned()));// ➞ "435-224-7613"
}

My answer is tweetable at 178 characters. Good luck reading this hacky regex that uses the resulting capture group's array index as the keypad's number.

textToNum = a => a.split("")
  .map(x => x.match(/()?([abc])?([def])?([ghi])?([jkl])?([mno])?([pqrs])?([tuv])?([wxyz])?/i)
  .reduce((a, c, i) => (i && c) ? i : a || c) || x).join("");


console.log(textToNum("123-647-EYES"), textToNum("123-647-EYES") == "123-647-3937")
console.log(textToNum("(325)444-TEST"), textToNum("(325)444-TEST") == "(325)444-8378")
console.log(textToNum("653-TRY-THIS"), textToNum("653-TRY-THIS") == "653-879-8447")
console.log(textToNum("435-224-7613"), textToNum("435-224-7613") == "435-224-7613")
console.log(textToNum("(33D)ONT-FAIL"), textToNum("(33D)ONT-FAIL") == "(333)668-3245")
console.log(textToNum("(025)445-6741"), textToNum("(025)445-6741") == "(025)445-6741")

Comments

  • Wow, using the capture group index... magnifiqué!