Related
(later edit)
Just finished this assignment. I was limited to using System and using System.collections.Generic. Nothing else. I ended up using a List<> from the from the collections.Generic. and the principles of Polish Notation. After every "Encounter" of a operation sign (+-/*) I used the index - 1 and - 2 to get the previous 2 numbers and make the operation. Then I used .Insert to insert the result at the current index, afterwards I used .Remove to substract from the list the numbers and the operator which I just used and after I continued with the recursive function on the new list obtained starting with the index from 0 again. The polish notation articles posted in the comments helped me the most to understand the logic behind this
*
I'm trying to figure out from the past several days a way to implement the following logic into a c# program. Using only System.
This is a small console calculator where the input is inserted on a single line in the console.
For example the following input + / * + 65 32 46 2 - 1 1.25 should be translated into a math operation looking like this => ((65 + 32) * 46) / 2 + (1 - 1.25)
Another example would be * + 3 2 - 9.5 6.5: this should be calculated in the following order 3 + 2 * (9.5 - 6.5).
Another example / + 5 3 2 equals with => (5 + 3) / 2
I have to make the function recursive.
I figured out how to make it if all the operations sings are in front of the digits in the input. (I just separate the operator list and inverse it and I get two separate lists: one containing the operation signs and the other containing the numbers). What I'm struggling is to figure out a way to do the operations if there is a math sign in between the numbers (like in the first an the second example).
I don't necessarily need a code for this, maybe an explanation or if somebody could point me to the right direction where I can read about some algorithm / math formula or something that could help me better understand how to implement this.
Thank you in advance.
The normal method of evaluating Polish Notation expressions doesn't require recursion, you use a stack (like Forth or RPN) and evaluate as you go.
An easy way to create a recursive version is to consider the expression language BNF then crafting a recursive descent parser from the grammer.
For example, a possible BNF would be:
expr = op arg arg
op = [+-*/] // cheating; use regex to describe terminal
arg = number | expr
number = [0-9]+ // using Regex to describe terminal
So now you would create methods for each element:
double expr() {
string opStr = op();
double arg1 = arg();
double arg2 = arg();
double ans;
switch (opStr) {
case "+":
ans = arg1+arg2;
break;
// case and so on
}
return ans;
}
static string operators = "+-*/";
string op() {
if (operators.Contains(peekChar()))
return nextCharAsStr();
else
throw new Exception("Missing operator");
}
double arg() {
double? num = number();
if (num.HasValue)
return num.Value;
else
return expr();
}
double? number() {
string ans = "";
while (Char.IsDigit(peekChar()))
ans += nextCharAsStr();
if (String.IsNullOrEmpty(ans))
return null;
else
return Double.Parse(ans);
}
NOTE: Whitespace and end of string is left as an exercise to the reader.
You could also use a tokenizer that extracts terminals from the string instead of working directly with characters in the parser terminal methods.
There are different kinds of recursion. The most common (code recursion) is probably what you're asking about, wherein a function (or set of functions) call each other until some sort of exit condition is reached.
For this, I'd go with more of a data recursive approach. This version only supports single-digit operands and binary operators.
(Really bad pseudocode below).
stack<char> operators;
stack<char> operands;
for(var i=input.Length-1; i>=0; --i)
{
var c = input[i];
if (Char.IsDigit(c)) /// note that this only handles single-digit numbers.
operands.push(c);
else
operators.push(c);
if (operators.count >= 1 && operands.count >= 2)
{
var operator = operators.pop();
/// handle binary operators
left = operands.pop();
right = operands.pop();
switch(operator) {
case '+' : result = left + right; break;
case '-' : result = left - right; break;
case '*' : result = left * right; break;
case '/' : result = left / right; break;
}
operands.push(result);
}
}
var result = operands.pop();
When that completes, your operands stack should have only one item which is the result of the expression. and operators should be empty.
If you have leftover operators, then there weren't enough values in the input. If you have more than one value in operands, there weren't enough operators in the input. If you have zero operands (ie, no result), then there weren't any in the input to start with.
For a real implementation, you'd want to parse the string to get multi-digit operands, handle unary operators, ignore whitespace, etc.
Edit: reversed the loop direction.
So you want something like this:
static void Main(string[] args)
{
Expression expr = Parse("+ / * + 65 32 46 2 - 1 1.25");
Console.WriteLine($"f = {expr}");
// f = ((((65 + 32) * 46) / 2) + (1 - 1.25))
Func<double> f = Compile(expr);
Console.WriteLine($"f = {f()}");
// f = 2230.75
}
public static Func<double> Compile(Expression expr)
{
return Expression.Lambda(expr).Compile() as Func<double>;
}
public static Expression Parse(string input)
{
var tokens = new Tokenizer(input);
return Parse(tokens);
}
There are two parts this.
First split the input into substrings using the white space. Use input.Split() for that. Then, scan the parts from right to left (from last to first) and extract tokens. A token is a substring that might represent a number or an operation (or a variable if you extend the code below). This is the job of Tokenizer class, the implements IEnumerable<TokenInfo> to act as a collection. A TokenInfo is a data type that describes each token, and it contains the following:
An identifying tag of enum Token.Number, Token.Operator or Token.End
The string text identifying the operator (when applicable)
The double value representing a number (when applicable)
public enum Token
{
Unknown,
Number,
Operator,
End
}
public struct TokenInfo
{
public TokenInfo(Token token)
{
Token=token;
Text = string.Empty;
Value = 0;
}
public TokenInfo(string op) : this(Token.Operator)
{
Text = op;
}
public TokenInfo(double value) : this(Token.Number)
{
Value = value;
}
public TokenInfo(Token token, string text) : this(token)
{
Text = text;
}
public Token Token { get; }
public string Text { get; }
public double Value { get; }
}
public class Tokenizer : IEnumerable<TokenInfo>
{
readonly string[] operators = new[] { "+", "-", "*", "/" };
readonly List<TokenInfo> tokens;
public Tokenizer(string input)
{
tokens = new List<TokenInfo>();
string[] parts = input.Split(' ');
for (int i = parts.Length - 1; i >= 0; i--)
{
if (operators.Contains(parts[i]))
{
tokens.Add(new TokenInfo(parts[i]));
}
else if (double.TryParse(parts[i], out var x))
{
tokens.Add(new TokenInfo(x));
}
else
{
tokens.Add(new TokenInfo(Token.Unknown, parts[i]));
}
}
tokens.Add(new TokenInfo(Token.End));
}
public IEnumerator<TokenInfo> GetEnumerator()
{
return tokens.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Parse the list of tokens and built the expression tree. Keep a Stack<Expression> and assume the top expression is always the result of the previous operation. Also what gets returned in the end is the top of the stack, if it is only one expression tall. Otherwise, there is an imbalance in the operators and/or the values.
Do the following for each Token type
Token.Number parse the value of the number using double.TryParse() and add a ConstantExpression on the stack
Token.Operator pull two values from the top of the stack and add an appropriate BinaryEpression to the stack. Possible options are Expression.Add(), Expression.Subtract(), Expression.Multiply(), and Expression.Divide()
Token.End return the last expression of the top of the stack.
public static Expression Parse(IEnumerable<TokenInfo> tokens)
{
Stack<Expression> stack = new Stack<Expression>();
foreach (var item in tokens)
{
switch (item.Token)
{
case Token.Number:
{
var expr = Expression.Constant(item.Value, typeof(double));
stack.Push(expr);
}
break;
case Token.Operator:
{
var arg1 = stack.Pop();
var arg2 = stack.Pop();
switch (item.Text)
{
case "+":
stack.Push(Expression.Add(arg1, arg2));
break;
case "-":
stack.Push(Expression.Subtract(arg1, arg2));
break;
case "*":
stack.Push(Expression.Multiply(arg1, arg2));
break;
case "/":
stack.Push(Expression.Divide(arg1, arg2));
break;
default:
throw new NotSupportedException($"Invalid operator {item.Text}.");
}
}
break;
case Token.End:
{
if (stack.Count != 1)
{
throw new InvalidOperationException("Imbalanced expression tree.");
}
return stack.Pop();
}
}
}
throw new ArgumentException($"Invalid expression.", nameof(tokens));
}
Optionally the Expression can be compiled into a function, using the Compile() function above. This code assumes there are no ParameterExpression nodes in the expression tree which would need to be accounted for as arguments to the compiled function.
The addition of variables in the expression and the subsequent compilation into unary or binary functions is pretty straightforward. Add a Token for a variable, produce an appropriate TokenInfo when a string that is not an operator is encountered in the tokenizer and then add ParameterExpression objects to the stack, just like numeric values are added. The compilation parts need references to these parameters from the tree so a custom ExpressionVistor would be needed to extract those nodes.
I'm learning about recursion and I want to create a function to type something and give me the same string but inverse but can't find the logic for it.
public static string Reverse(string text, int textlength)
{
if (textlength== 0)
{
return text.ElementAt(textlength).ToString();
}
else
{
return Reverse(text, textlength - 1);
}
}
How could I do that using recursion?
Recursion is always about two cases, the base case and the recursive case.
The base case is when the string is 0 or 1 characters - the reverse is just what was passed in, so we return text.
The recursive case is when text is longer than 1 character, we must define the operation in terms of itself. In this case, the reverse of the string is the reverse of the rest of the string followed by the first character.
public static string Reverse(string text) {
if (text.Length <= 1) // base case
return text;
else // recursive case
return Reverse(text.Substring(1))+text.Substring(0, 1);
}
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 translate the number to its numeric equivalent.
For example: A, B, C = 2; D, E, F = 3; etc...
We have not covered Classes or creating maps at this point so these would not be viable solutions. This chapter does cover the enum so I am working to solve using an Enumerated type. I have a method set up to validate the data (ensure the correct # of characters and the hyphens are in the correct place) and this does work correctly. I have another method set up to remove the hyphens and also uses the ToUpper() method and this also works correctly, so the foreach loop I have set up us using the number after these two methods have already finished.
I have also set up a method to run after the conversion takes place to add the hyphens back in and this also works.
I have tried several ways to get is to work and have left them in commented out on the chance they might be what I need to use, for my attempt with the switch statement I only set up with the letter A for now, and plan to finish the remaining letters if I am able to get this to work. I think the one my issues is the foreach loop is using a char type and the switch is using an int. Seems to be the same issue when trying the code inside the foreach loop but I am not sure how to fix so any suggestions are appreciated.
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
}
private void UpdatePhone(AlphaNumber phone)
{
switch (phone)
{
case AlphaNumber.A:
//return AlphaNumber.(int[])Enum.GetValues(typeof(AlphaNumber));
//return (Enum.GetValues(typeof(AlphaNumber)));
// (int)ValueType;
Enum.GetValues(typeof(AlphaNumber));
break;
}
private void translateButton_Click(object sender, EventArgs e)
{
numberLabel.Text = "";//Clear the numberLabel
//Get a trimmed copy of the user's input.
string input = numberTextBox.Text.Trim();
if (IsValidFormat(input))
{
Unformat(ref input);
foreach (char ch in input)
{
if (char.IsLetter(ch))// {
ch = (char)Enums.AlphaNumber.Active;
//ch = (char)Enum.GetValues(typeof(AlphaNumber));
//ch = Enum.TryParse(AlphaNumber);
// ch = AlphaNumber.(int[])Enum.GetValues(typeof(AlphaNumber));
//UpdatePhone(ch);
MessageBox.Show("Character is char"); }
}
TelephoneFormat(ref input);
numberLabel.Text = input;
I am open to any suggestions and appreciate any help provided. Where I need help is inside the foreach loop, I am looking at each value and if it is a letter I want to get the value from the enum values and replace the char with the number value.
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';
}
}
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)))
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);
}
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;
}
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;
}
I am looking to return the bool containsVowel into my main method, where if a word contains a vowel it will allow it to be stored into an array. When I use the code below an error comes up over the method name saying "not all code paths return a value". I've also tried not using the for loop, as well as the char array - it works fine then but if I enter a vowel into the form it still says no vowel has been found.
private bool vowel(string word)
{
bool containsVowel = false;
char[] wordChar = word.ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (wordChar[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return containsVowel = true;
break;
default:
MessageBox.Show("Word must contain a vowel", "Error");
return containsVowel = false;
break;
}
}
}
Given your method has the return type bool instead of void, all code paths must return a value. The C# compiler tries to very naively evaluate all code paths to see if your method returns a value from all paths.
Your method looks like this to the compiler:
start
for
switch
case
return
default
return
end
The switch is covered alright thanks to the default, but the for is a conditional branch as well. If you pass an empty string, the for won't be entered (i = 0 and word.Length = 0, so i < word.Length is false from the beginning), so after the for, there needs to be a return statement as well.
You can massively improve the code by doing the following:
public bool ContainsVowel(string word)
{
if (string.IsNullOrEmpty(word))
{
return false;
}
var upperCaseCharacters = word.ToUpper().ToCharArray();
foreach (var character in upperCaseCharacters)
{
switch (character)
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return true;
}
}
return false;
}
This code checks whether the string being passed is null or empty, and then immediately returns false (fail fast). Then it converts the string to upper case once, and loops over the character array with a foreach(), which is the preferred way to iterate over a collection anyway. Then it doesn't use an intermediate variable to store the result, as you can return true as soon as you encounter a vowel.
Finally, when no vowel has been encountered after iterating over all characters, the method returns false.
Then at the call site, you can do this:
if (!ContainsVowel(word))
{
MessageBox.Show("Word must contain a vowel", "Error");
}
Because methods like this should not contain UI logic such as message boxes.
This code should do.First, check for a null word value.Then you must convert your word to upper in order to detect all vowels, being this upper or lower. Then, you must only check for a vowel, because if one is present the method should return true.If,after checking all the letters, the method has not detected a vowel,show error message and return false :
private static bool vowel(string word)
{
if (word == null) return false;
char[] wordChar = word.ToUpper().ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (word[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return true;
break;
}
}
Console.WriteLine("Word must contain a vowel");
return false;
}
As you can see, this method always returns a value. In your code, if the word passed is empty,it would not enter the forloop, so it won't return anything.
Your code only checked the first character of the string, since it jumped out of the loop if it was vowel or not. This should fix it, but for a better approach, check #Sr82's answer
private bool vowel(string word)
{
bool containsVowel = false;
char[] wordChar = word.ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (wordChar[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return containsVowel = true;
//if you find a vowel, you can return true straight away
}
}
//if you dont find one, let the for loop finish. If it reaches this code you know
//there was no vowel found and you can proceed with your error report
MessageBox.Show("Word must contain a vowel", "Error");
return containsVowel = false;
}
This simple method should work if you can use Linq. The Intersect method takes two char sequences and returns a sequence of char that exists in both of them. The Any method returns true if there is anything in the sequence.
using System.Linq;
void Main()
{
string s = "Word";
if (hasVowels(s))
{
Console.WriteLine("Word has vowels");
}
else
{
Console.WriteLine("Word does not have vowels");
}
}
//The `Intersect` method takes two `char` sequences and returns a sequence
//of `char` that exists in both of them. The `Any` method returns true if
//there is anything in the resulting sequence.
private bool hasVowels(string word)
{
string vowels = "AEIOUaeiou";
return word != null && word.Intersect(vowels).Any();
}
If you're trying to check whether a string contains a vowel and that's it, you can do something like this:
private bool vowel(string word)
{
return word.ToLower().Any(x => "aeiou".Contains(x));
}
This uses LINQ and specifically the Any method to determine if the string contains any vowels. It would be good to set the string to all lower case so you can check whether a vowel is present irrespective of case.
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 translate the number to its numeric equivalent.
For example: A, B, C = 2; D, E, F = 3; etc...
We have not covered Classes or creating maps at this point so these would not be viable solutions. This chapter does cover the enum so I am working to solve using an Enumerated type. I have a method set up to validate the data (ensure the correct # of characters and the hyphens are in the correct place) and this does work correctly. I have another method set up to remove the hyphens and also uses the ToUpper() method and this also works correctly, so the foreach loop I have set up us using the number after these two methods have already finished.
I have also set up a method to run after the conversion takes place to add the hyphens back in and this also works.
I have tried several ways to get is to work and have left them in commented out on the chance they might be what I need to use, for my attempt with the switch statement I only set up with the letter A for now, and plan to finish the remaining letters if I am able to get this to work. I think the one my issues is the foreach loop is using a char type and the switch is using an int. Seems to be the same issue when trying the code inside the foreach loop but I am not sure how to fix so any suggestions are appreciated.
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
}
private void UpdatePhone(AlphaNumber phone)
{
switch (phone)
{
case AlphaNumber.A:
//return AlphaNumber.(int[])Enum.GetValues(typeof(AlphaNumber));
//return (Enum.GetValues(typeof(AlphaNumber)));
// (int)ValueType;
Enum.GetValues(typeof(AlphaNumber));
break;
}
private void translateButton_Click(object sender, EventArgs e)
{
numberLabel.Text = "";//Clear the numberLabel
//Get a trimmed copy of the user's input.
string input = numberTextBox.Text.Trim();
if (IsValidFormat(input))
{
Unformat(ref input);
foreach (char ch in input)
{
if (char.IsLetter(ch))// {
ch = (char)Enums.AlphaNumber.Active;
//ch = (char)Enum.GetValues(typeof(AlphaNumber));
//ch = Enum.TryParse(AlphaNumber);
// ch = AlphaNumber.(int[])Enum.GetValues(typeof(AlphaNumber));
//UpdatePhone(ch);
MessageBox.Show("Character is char"); }
}
TelephoneFormat(ref input);
numberLabel.Text = input;
I am open to any suggestions and appreciate any help provided. Where I need help is inside the foreach loop, I am looking at each value and if it is a letter I want to get the value from the enum values and replace the char with the number value.
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';
}
}
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)))
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);
}
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;
}
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;
}