Tuesday 5 January 2016

Using C# The Cheque (Check) Writer


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.


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;
        }

    }
}


No comments:

Post a Comment