I have a homework assignment where the program will accept any phone number in the format similar to 555-GET-FOOD. The task is to map the alphabetic letters to numbers and tran
Here are some options for simple logic to process phone numbers as described. Note that while a novel approach, using an Enum to map letters to integer values somewhat complicates the process more than it needed to be.
Even though other methods exist for specifically mapping alpha-numeric phone numbers (think Regex) if you do ever need to iterate or process using Enum Names and Values then I hope you find these algorithms useful.
A dictionary would have been far simpler as a mapping construct to process from.
Regex where the LetterNumber mapping is defined in the expression would have been superior again.
/// <summary>
/// Iterates through the characters in a phone number converting letters to digits.
/// </summary>
/// <remarks>Uses StringBuilder to build the output iteratively, this method does not attempt to validate the number passed in</remarks>
/// <see cref="LetterNumber"/>
/// <param name="str">Phone number to parse</param>
/// <returns>Phone number output where letters have been parsed into their digit values</returns>
private string ParsePhoneNumber_StringBuilder(string str)
{
StringBuilder output = new StringBuilder();
foreach (char ch in str.ToCharArray())
{
// Convert each letter to it's numeric value as defined in the LetterNumber enum
// Dashes are not letters so they will get passed through
if (char.IsLetter(ch))
{
if (Enum.IsDefined(typeof(LetterNumber), ch.ToString()))
{
LetterNumber letterNumber = (LetterNumber)Enum.Parse(typeof(LetterNumber), ch.ToString(), true);
output.Append((int)letterNumber);
}
}
else
output.Append(ch);
}
return output.ToString();
}
/// <summary>
/// Uses Linq to parse the characters in a phone number converting letters to digits.
/// </summary>
/// <remarks>This method does not attempt to validate the number passed in</remarks>
/// <see cref="LetterNumber"/>
/// <param name="str">Phone number to parse</param>
/// <returns>Phone number output where letters have been parsed into their digit values</returns>
private string ParsePhoneNumber_Linq(string str)
{
return String.Join("", str.Select(c => char.IsLetter(c) ? ((int)((LetterNumber)Enum.Parse(typeof(LetterNumber), c.ToString(), true))).ToString() : c.ToString()));
}
/// <summary>
/// Iterates through the LetterNumber values and replaces values found in the passed in phone number.
/// </summary>
/// <remarks>Iterates through Enum Names and applied String.Replace</remarks>
/// <see cref="LetterNumber"/>
/// <param name="str">Phone number to parse</param>
/// <returns>Phone number output where letters have been parsed into their digit values</returns>
private string ParsePhoneNumber_Replacement(string str)
{
str = str.ToUpper(); // we will compare all letters in upper case
foreach (string letter in Enum.GetNames(typeof(LetterNumber)))
str = str.Replace(letter.ToUpper(), ((int)((LetterNumber)Enum.Parse(typeof(LetterNumber), letter))).ToString());
return str;
}
It looks like you did attempt to use TryParse
:
//ch = Enum.TryParse(AlphaNumber);
That is the method that you probably want to use. But there are a number of problems with it in that form, which probably gave you errors as you mentioned.
The parameters this method expects are a string
(which matches the enumerated constant, or name from the enum
) and an out
parameter of the type of the enum
you want to parse. The method return a bool
.
If the TryParse
is successful then the method returns TRUE with the corresponding value from the enum
set in the out
parameter.
This code should allow you to get the result you want as an int
using your variable ch
as a string
for the input to parse:
AlphaNumber parsedCh;
int? chValue = null;
if (Enum.TryParse(ch.ToString().ToUpper(), out parsedCh))
{
chValue = (int)parsedCh;
Console.WriteLine(chValue);
}
Drop the enum
and the switch
, and instead do a lookup against a map. I have created a .NET Fiddle that demonstrates this working as desired here.
But if using a Dictionary<char, char>
is for some odd reason out of question, then I suppose you could use this version -- in which I use the enum
. It is just a lot more work and the more moving parts you have the harder it is to maintain.
In C#
the type enum
inherits from int
by default, but you can specify various other numeric types -- like byte
or long
, etc... One of the key takeaways is the concept of boxing/unboxing and casting/converting from one type to another. If you have an enum
defined as enum Foo { Bar = 63 }
and you try to cast
it to a char
what would you expect to get?
This actually would result in the char ?
-- take a look at the ASCII table and the find the DEC
for 63 and look at what it maps to for the Char
column.
The issue is fixed with this crazy complicated mess:
public enum AlphaNumber
{
A=2, B=2, C=2, D=3, E=3, F=3, G=4, H=4, I=4, J=5, K=5, L=5,
M=6, N=6, O=6, P=7, Q=7, R=7, S=8, T=8, U=8, V=9, W=9, X=9, Y=9, Z=9
}
public static class PhoneNumber
{
public static char ParseInput(char input)
{
if (input == '-' || char.IsDigit(input))
{
return input;
}
if (char.IsLetter(input))
{
var num = (AlphaNumber)(Enum.Parse(typeof(AlphaNumber), (char.IsLower(input) ? char.ToUpperInvariant(input) : input).ToString()));
return ((int)num).ToString()[0];
}
return '\0';
}
}
using System;
public class Program
{
public static void Main()
{
Console.WriteLine(AlphaPhoneToNumber("555-GET-FOOD"));
}
public static string AlphaPhoneToNumber(string val){
//strings are immutable so let's get the array version of the value
var phoneNumberArr = val.ToCharArray();
for(int i = 0; i < val.Length; i++){
phoneNumberArr[i] = AlphaPhoneCharToNumber(phoneNumberArr[i]);
}
return new string(phoneNumberArr);
}
public static char AlphaPhoneCharToNumber(char val){
switch(val){
case 'A':
case 'B':
case 'C':
return '2';
case 'D':
case 'E':
case 'F':
return '3';
case 'G':
case 'H':
case 'I':
return '4';
case 'J':
case 'K':
case 'L':
return '5';
case 'M':
case 'N':
case 'O':
return '6';
case 'P':
case 'Q':
case 'R':
return '7';
case 'S':
case 'T':
case 'U':
return '8';
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
return '9';
default: return val;
}
}
}
and here's how to do it without switch:
public static char AlphaPhoneCharToNumber2(char val){
// All three arrays are of exact same length
var ualphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
var lalphas = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
var numval = "22233344455566677788899999".ToCharArray();
// thus I will loop up to the max length of one of the arrays
// remember they are all the same length
for(int i = 0; i < ualphas.Length; i++){
//I will check if the value is in one of the alphabet
//arrays at the current index
//if there is a match I will assign it the number value
//at the same index
// MATCH UPPER? MATCH LOWER? RETURN NUMBER
if(ualphas[i] == val || lalphas[i] == val) return numval[i];
}
// ELSE RETURN THE ORIGINAL VALUE
return val;
}
While "map" (implemented as Dictionary
in C#/.Net) is probably best choice for this problem, basic math is enough for such simple transformation:
Console.WriteLine(String.Join("", // combine characters back to string
"555-GET-FOOD" //value
.ToUpperInvariant() // carefully converting to avoid Turkish I problems
.Select(c=> (c>='A' && c<='Z') ? // only tweak A-Z range
Math.Min((c-'A')/3+2,9).ToString()[0] : c)))