Using C# The Cheque (Check) Writer
Recently I was given a test to test my coding skills not just functionally but also aesthetically as well. My wife is an art teacher and I always believe coding is an art form as well as an engineering discipline.
So I decided to blog and plan to seek from the IT community any feedback on the infinite possible ways to tackle the solution.
The test was to write a cheque printing application that needs to be able to
display numeric values on the screen using the equivalent text. So I went ahead and design and
implement an application that does take a numerical value for a cheque and displays
it in words on the screen.
For example if I entered
1234.56
The program would return
one thousand two hundred and thirty four dollars AND fifty six cents
As a team leader in my previous roles I have done many interviews. I have in the past used this test without much success and it usually only done by candidates who have gone through and succeeded simpler tasks.
I have to be fair I feel my solution could be better hence why I have blogged and interested in feedback regarding this problem.
For example if I entered
1234.56
The program would return
one thousand two hundred and thirty four dollars AND fifty six cents
As a team leader in my previous roles I have done many interviews. I have in the past used this test without much success and it usually only done by candidates who have gone through and succeeded simpler tasks.
I have to be fair I feel my solution could be better hence why I have blogged and interested in feedback regarding this problem.
The approach I have taken is to
split and group the number based on their significant values i.e. their
numerical significance. For example:
1234.56
Will be grouped in the
following way
1 thousands represented
as one thousand.
2 hundreds represented
as two hundred.
3 tens represented as
thirty.
4 units represented as
four.
Then the cents are
grouped in the same manner.
Hence the output will be one thousand two hundred
and thirty four dollars AND fifty six cents
The text are kept in
string arrays indexed by the quotient for efficiency in lookup and translation
from number to text.
i.e. 200 / 100 will be 2.
The array will
arrUnits[2] which returns a string two etc.. as per initialisation.
This allows for easy adjustment to another language which will require a small code change and initialisation differently.
I include the code below with comments. I welcome comments and likely will get the community to provide possible solutions.
Anyone thoughts would be appreciated.
The link is https://www.dropbox.com/s/7cte6kdonky4i0d/ChequeWriter.zip?dl=0
Whilst the actual code is included below.
using System;
using System.Collections.Generic;
using System.Text;
namespace ChequeWriter
{
/// <summary>
/// This class performs the number to written text functionality.
/// It exposes one public member function OutputNumberToString
/// Implementation:
/// All Cheque Writing Texts are stored indexable arrays.
/// I choose this over collections/lists simply because of memory efficiency.
///
/// The arrays are initialised in the constructor.
/// </summary>
public class ChequeWriter
{
// Array Containing Currency Dollar / Cents easily adaptable to Pounds
string[] strarrCurrency;//[4];
// Array Containing Singular Unit Texts
string[] strarrUnits;//[10];
// Array Containing DecaUnits and Teens
string[] strarrTens;//[20];
// Array Containing HundredUnits
// Reproduction of strarrUnits and deemed not necessary but kept as I coded the solution. Shows how code can adapt to language if necessary.
string[] strarrHundreds;//[10];
// Array Containing Thousandths.
string[] strarrThousands;//[3];
/// <summary>
/// The arrays are initialised and language dependent texts are populated in the array.
/// </summary>
public ChequeWriter()
{
strarrCurrency = new string[4];
strarrUnits = new string[10];//[10];
strarrTens = new string[20];//[20];
strarrHundreds = new string[10];//[10];
strarrThousands = new string[3];//[3];
strarrCurrency[0] = "dollars";
strarrCurrency[1] = "dollar";
strarrCurrency[2] = "cents";
strarrCurrency[3] = "cent";
strarrUnits[0] = "";
strarrUnits[1] = "one";
strarrUnits[2] = "two";
strarrUnits[3] = "three";
strarrUnits[4] = "four";
strarrUnits[5] = "five";
strarrUnits[6] = "six";
strarrUnits[7] = "seven";
strarrUnits[8] = "eight";
strarrUnits[9] = "nine";
strarrTens[0] = "";
strarrTens[1] = "ten";
strarrTens[2] = "twenty";
strarrTens[3] = "thirty";
strarrTens[4] = "fourty";
strarrTens[5] = "fifty";
strarrTens[6] = "sixty";
strarrTens[7] = "seventy";
strarrTens[8] = "eighty";
strarrTens[9] = "ninety";
strarrTens[10] = "ten";
strarrTens[11] = "eleven";
strarrTens[12] = "twelve";
strarrTens[13] = "thirteen";
strarrTens[14] = "fourteen";
strarrTens[15] = "fifteen";
strarrTens[16] = "sixteen";
strarrTens[17] = "seventeen";
strarrTens[18] = "eighteen";
strarrTens[19] = "nineteen";
strarrHundreds[0] = "";
strarrHundreds[1] = "one";
strarrHundreds[2] = "two";
strarrHundreds[3] = "three";
strarrHundreds[4] = "four";
strarrHundreds[5] = "five";
strarrHundreds[6] = "six";
strarrHundreds[7] = "seven";
strarrHundreds[8] = "eight";
strarrHundreds[9] = "nine";
strarrThousands[0] = "thousand ";
strarrThousands[1] = "million ";
strarrThousands[2] = "billion ";
}
/// <summary>
/// The console application entry point. The user is prompted for entering a number.
/// This number will be then converted into text.
/// The ChequeWriter class is used to provide this functionality.
/// </summary>
static void Main(string[] args)
{
string strName;
string strInput;
long lngWholeInput;
int intCentsInput;
ChequeWriter ntsOutput = new ChequeWriter();
do
{
// User input is read in.
Console.Write("Please Enter Your Name ");
strName = Console.ReadLine();
Console.Write("Please Enter a number ");
strInput = Console.ReadLine();
// Output Verbatim The User Name
Console.WriteLine(strName);
// User can enter cents and is optional hence the checking of the decimal point.
if (strInput.IndexOf('.') > 0)
{
// Validation and Parsing of Input as Numeric before proceeding.
// First Parse Whole Amount of User Input.
// Not in requirements but could have need for a custom response.
if (long.TryParse(strInput.Substring(0, strInput.IndexOf('.')), out lngWholeInput))
{
// Parse Decimal Portion of User Input.
// Proceed and call function and Output Number to String
if (int.TryParse(strInput.Substring(strInput.IndexOf('.') + 1), out intCentsInput))
Console.WriteLine(ntsOutput.OutputNumberToString(lngWholeInput, intCentsInput));
}
}
else
{
// Parse Input as Whole Amount
// Proceed and call function and Output Number to String
if (long.TryParse(strInput, out lngWholeInput))
Console.WriteLine(ntsOutput.OutputNumberToString(lngWholeInput, 0));
}
}
// Repeat While User Input
while ((strInput.Length > 0) && (strName.Length > 0));
}
/// <summary>
/// Output Number To String - Public
/// Converts a number provided into text.
/// </summary>
/// <param name="lngWholeValue"></param>
/// <param name="intCentsValue"></param>
/// <returns>String in words of input value as a cheque writer</returns>
public string OutputNumberToString(long lngWholeValue, int intCentsValue)
{
string strOutput = "";
long lngRemainder;
// Calculate the indexes for retrieving text from the array.
long iBillion = Math.DivRem((long)lngWholeValue, (long)1000000000, out lngRemainder);
long iMillion = Math.DivRem((long)lngRemainder, (long)1000000, out lngRemainder);
long iThousand = Math.DivRem((long)lngRemainder, (long)1000, out lngRemainder);
long iUnits = lngRemainder;
if (iBillion > 0)
strOutput += OutputHundredUnitsToString(System.Convert.ToInt32(iBillion)) + strarrThousands[2];
if (iMillion > 0)
strOutput += OutputHundredUnitsToString(System.Convert.ToInt32(iMillion)) + strarrThousands[1];
if (iThousand > 0)
strOutput += OutputHundredUnitsToString(System.Convert.ToInt32(iThousand)) + strarrThousands[0];
if (iUnits > 0)
strOutput += OutputHundredUnitsToString(System.Convert.ToInt32(iUnits)) + strarrCurrency[0];
else
strOutput += strarrCurrency[0];
if (intCentsValue > 0)
strOutput += " AND " + OutputHundredUnitsToString(System.Convert.ToInt32(intCentsValue)) + strarrCurrency[2];
else
strOutput += " AND ZERO CENTS";
return strOutput;
}
/// <summary>
///
/// </summary>
/// <param name="iValue"></param>
/// <returns>Cheque Writing within the range of 1 to 999 </returns>
private string OutputHundredUnitsToString(int iValue)
{
string strOutput = "";
int iTens; // The number calculated by output of a remainder for hundreds.
int iUnits; // The number calculated by output of a remainder for tens.
// Math Div Rem Allow Quotient and Remainder in 1 function call.
// It is an optimization. Some processors are able to compute both values at the same time.
// Number of hundred with remainder.
// Hundreds are converted to text.
if (Math.DivRem(iValue, 100, out iTens) > 0)
strOutput += strarrHundreds[(iValue / 100)] + " hundred ";
// Number of tens with remainder.
// Tens are converted to text.
if (Math.DivRem(iTens, 10, out iUnits) > 0)
{
if (strOutput.Length > 0)
strOutput += "and ";
if ((iTens / 10) > 1)
strOutput += strarrTens[(iTens / 10)] + " ";
else
strOutput += strarrTens[iTens] + " ";
}
// Remainder Units are converted to text
if ((iUnits / 1) > 0)
{
if (((iValue / 100) > 0) && ((iTens / 10) == 0))
strOutput += "and ";
if ((iTens >= 20) || (iTens <= 9))
strOutput += strarrUnits[(iUnits / 1)] + " ";
}
return strOutput;
}
}
}
using System.Collections.Generic;
using System.Text;
namespace ChequeWriter
{
/// <summary>
/// This class performs the number to written text functionality.
/// It exposes one public member function OutputNumberToString
/// Implementation:
/// All Cheque Writing Texts are stored indexable arrays.
/// I choose this over collections/lists simply because of memory efficiency.
///
/// The arrays are initialised in the constructor.
/// </summary>
public class ChequeWriter
{
// Array Containing Currency Dollar / Cents easily adaptable to Pounds
string[] strarrCurrency;//[4];
// Array Containing Singular Unit Texts
string[] strarrUnits;//[10];
// Array Containing DecaUnits and Teens
string[] strarrTens;//[20];
// Array Containing HundredUnits
// Reproduction of strarrUnits and deemed not necessary but kept as I coded the solution. Shows how code can adapt to language if necessary.
string[] strarrHundreds;//[10];
// Array Containing Thousandths.
string[] strarrThousands;//[3];
/// <summary>
/// The arrays are initialised and language dependent texts are populated in the array.
/// </summary>
public ChequeWriter()
{
strarrCurrency = new string[4];
strarrUnits = new string[10];//[10];
strarrTens = new string[20];//[20];
strarrHundreds = new string[10];//[10];
strarrThousands = new string[3];//[3];
strarrCurrency[0] = "dollars";
strarrCurrency[1] = "dollar";
strarrCurrency[2] = "cents";
strarrCurrency[3] = "cent";
strarrUnits[0] = "";
strarrUnits[1] = "one";
strarrUnits[2] = "two";
strarrUnits[3] = "three";
strarrUnits[4] = "four";
strarrUnits[5] = "five";
strarrUnits[6] = "six";
strarrUnits[7] = "seven";
strarrUnits[8] = "eight";
strarrUnits[9] = "nine";
strarrTens[0] = "";
strarrTens[1] = "ten";
strarrTens[2] = "twenty";
strarrTens[3] = "thirty";
strarrTens[4] = "fourty";
strarrTens[5] = "fifty";
strarrTens[6] = "sixty";
strarrTens[7] = "seventy";
strarrTens[8] = "eighty";
strarrTens[9] = "ninety";
strarrTens[10] = "ten";
strarrTens[11] = "eleven";
strarrTens[12] = "twelve";
strarrTens[13] = "thirteen";
strarrTens[14] = "fourteen";
strarrTens[15] = "fifteen";
strarrTens[16] = "sixteen";
strarrTens[17] = "seventeen";
strarrTens[18] = "eighteen";
strarrTens[19] = "nineteen";
strarrHundreds[0] = "";
strarrHundreds[1] = "one";
strarrHundreds[2] = "two";
strarrHundreds[3] = "three";
strarrHundreds[4] = "four";
strarrHundreds[5] = "five";
strarrHundreds[6] = "six";
strarrHundreds[7] = "seven";
strarrHundreds[8] = "eight";
strarrHundreds[9] = "nine";
strarrThousands[0] = "thousand ";
strarrThousands[1] = "million ";
strarrThousands[2] = "billion ";
}
/// <summary>
/// The console application entry point. The user is prompted for entering a number.
/// This number will be then converted into text.
/// The ChequeWriter class is used to provide this functionality.
/// </summary>
static void Main(string[] args)
{
string strName;
string strInput;
long lngWholeInput;
int intCentsInput;
ChequeWriter ntsOutput = new ChequeWriter();
do
{
// User input is read in.
Console.Write("Please Enter Your Name ");
strName = Console.ReadLine();
Console.Write("Please Enter a number ");
strInput = Console.ReadLine();
// Output Verbatim The User Name
Console.WriteLine(strName);
// User can enter cents and is optional hence the checking of the decimal point.
if (strInput.IndexOf('.') > 0)
{
// Validation and Parsing of Input as Numeric before proceeding.
// First Parse Whole Amount of User Input.
// Not in requirements but could have need for a custom response.
if (long.TryParse(strInput.Substring(0, strInput.IndexOf('.')), out lngWholeInput))
{
// Parse Decimal Portion of User Input.
// Proceed and call function and Output Number to String
if (int.TryParse(strInput.Substring(strInput.IndexOf('.') + 1), out intCentsInput))
Console.WriteLine(ntsOutput.OutputNumberToString(lngWholeInput, intCentsInput));
}
}
else
{
// Parse Input as Whole Amount
// Proceed and call function and Output Number to String
if (long.TryParse(strInput, out lngWholeInput))
Console.WriteLine(ntsOutput.OutputNumberToString(lngWholeInput, 0));
}
}
// Repeat While User Input
while ((strInput.Length > 0) && (strName.Length > 0));
}
/// <summary>
/// Output Number To String - Public
/// Converts a number provided into text.
/// </summary>
/// <param name="lngWholeValue"></param>
/// <param name="intCentsValue"></param>
/// <returns>String in words of input value as a cheque writer</returns>
public string OutputNumberToString(long lngWholeValue, int intCentsValue)
{
string strOutput = "";
long lngRemainder;
// Calculate the indexes for retrieving text from the array.
long iBillion = Math.DivRem((long)lngWholeValue, (long)1000000000, out lngRemainder);
long iMillion = Math.DivRem((long)lngRemainder, (long)1000000, out lngRemainder);
long iThousand = Math.DivRem((long)lngRemainder, (long)1000, out lngRemainder);
long iUnits = lngRemainder;
if (iBillion > 0)
strOutput += OutputHundredUnitsToString(System.Convert.ToInt32(iBillion)) + strarrThousands[2];
if (iMillion > 0)
strOutput += OutputHundredUnitsToString(System.Convert.ToInt32(iMillion)) + strarrThousands[1];
if (iThousand > 0)
strOutput += OutputHundredUnitsToString(System.Convert.ToInt32(iThousand)) + strarrThousands[0];
if (iUnits > 0)
strOutput += OutputHundredUnitsToString(System.Convert.ToInt32(iUnits)) + strarrCurrency[0];
else
strOutput += strarrCurrency[0];
if (intCentsValue > 0)
strOutput += " AND " + OutputHundredUnitsToString(System.Convert.ToInt32(intCentsValue)) + strarrCurrency[2];
else
strOutput += " AND ZERO CENTS";
return strOutput;
}
/// <summary>
///
/// </summary>
/// <param name="iValue"></param>
/// <returns>Cheque Writing within the range of 1 to 999 </returns>
private string OutputHundredUnitsToString(int iValue)
{
string strOutput = "";
int iTens; // The number calculated by output of a remainder for hundreds.
int iUnits; // The number calculated by output of a remainder for tens.
// Math Div Rem Allow Quotient and Remainder in 1 function call.
// It is an optimization. Some processors are able to compute both values at the same time.
// Number of hundred with remainder.
// Hundreds are converted to text.
if (Math.DivRem(iValue, 100, out iTens) > 0)
strOutput += strarrHundreds[(iValue / 100)] + " hundred ";
// Number of tens with remainder.
// Tens are converted to text.
if (Math.DivRem(iTens, 10, out iUnits) > 0)
{
if (strOutput.Length > 0)
strOutput += "and ";
if ((iTens / 10) > 1)
strOutput += strarrTens[(iTens / 10)] + " ";
else
strOutput += strarrTens[iTens] + " ";
}
// Remainder Units are converted to text
if ((iUnits / 1) > 0)
{
if (((iValue / 100) > 0) && ((iTens / 10) == 0))
strOutput += "and ";
if ((iTens >= 20) || (iTens <= 9))
strOutput += strarrUnits[(iUnits / 1)] + " ";
}
return strOutput;
}
}
}
No comments:
Post a Comment