Challenge: Number Letter Counts

Posted on: July 9, 2016 2:02:04 PM

This challenge wants you to get the count of the number of letters in the numbers 1 to 1000. This was a bit of an extra challenge for me because it wants the numbers in a "British compliance" format. This means the word 'and' is included in the number. I didn't do any research on the exact specifics on when the 'and' should be used, I just looked at their example of how to handle the hundreds because we're only trying to get to 1000.

IntToWordHelper.cs
using System.Collections.Generic;
using System.Text;

namespace ProjectEuler_17
{
    public static class IntToWordHelper
    {
        private const string And = "and";
        private const string Space = " ";

        private static readonly string[] BaseNumbers =
        {
            "zero",
            "one",
            "two",
            "three",
            "four",
            "five",
            "six",
            "seven",
            "eight",
            "nine",
            "ten",
            "eleven",
            "twelve",
            "thirteen",
            "fourteen",
            "fifteen",
            "sixteen",
            "seventeen",
            "eighteen",
            "nineteen"
        };

        private static readonly string[] HundredMultiplierNames =
        {
            null, // pad the array so the values line up properly
            "hundred",
            "thousand",
            "million",
            "billion"
        };

        private static readonly string[] TensNames =
                {
            null, // pad the array so the values line up properly
            null,
            "twenty",
            "thirty",
            "forty",
            "fifty",
            "sixty",
            "seventy",
            "eighty",
            "ninety"
        };

        public static string ConvertToWord(int number, bool includeAnd = false)
        {
            List result = new List();

            int[] intArray = GetIntArray(number);

            if (intArray.Length == 0)
            {
                return BaseNumbers[0];
            }

            if (intArray.Length == 1)
            {
                return BaseNumbers[intArray[0]];
            }

            int index = 0;
            int hundredsMultiplierIndex = 0;
            while (index < intArray.Length)
            {
                // take 3 numbers at a time
                int one = intArray[index++];
                int ten = index < intArray.Length ? intArray[index++] : 0;
                int hundred = index < intArray.Length ? intArray[index++] : 0;

                string words = ProcessNumber(hundred, ten, one, includeAnd);

                if (hundredsMultiplierIndex++ > 0 && !string.IsNullOrEmpty(words))
                {
                    words += Space + HundredMultiplierNames[hundredsMultiplierIndex];
                }

                if (!string.IsNullOrEmpty(words))
                {
                    result.Add(words);
                }
            }

            // the words are reversed at this point
            result.Reverse();
            return string.Join(Space, result);
        }

        private static int[] GetIntArray(int num)
        {
            List listOfInts = new List();
            while (num > 0)
            {
                listOfInts.Add(num % 10);
                num = num / 10;
            }

            // the array is reversed, but this helps
            return listOfInts.ToArray();
        }

        private static string ProcessNumber(int hundred, int ten, int one, bool includeAnd)
        {
            StringBuilder sb = new StringBuilder();

            if (hundred > 0)
            {
                sb.Append(BaseNumbers[hundred]).Append(Space).Append(HundredMultiplierNames[1]);

                if (includeAnd)
                {
                    if (ten > 0 || one > 0)
                    {
                        sb.Append(Space).Append(And);
                    }
                }

                sb.Append(Space);
            }

            if (ten > 0)
            {
                if (ten == 1)
                {
                    return sb.Append(BaseNumbers[(ten * 10) + one]).ToString();
                }

                sb.Append(TensNames[ten]).Append(Space);
            }

            if (one > 0)
            {
                sb.Append(BaseNumbers[one]);
            }

            return sb.ToString().Trim();
        }
    }
}

You'll notice that I handle the number in groups of 3, this is because hundreds are always labeled the same regardless of where it is. This allows me to use far less code than would otherwise be needed. The next bit of code needed is the actual counting of the words with the loop of numbers.

Program.cs
using System;

namespace ProjectEuler_17
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            int count = 0;
            for (int i = 1; i <= 1000; i++)
            {
                string word = IntToWordHelper.ConvertToWord(i, true);

                count += word.Replace(" ", "").Length;
            }

            Console.WriteLine("Count: {0}", count);
        }
    }
}

Lastly, as a good practice, some unit tests to prove the functionality.

Tests.cs
using NUnit.Framework;

namespace ProjectEuler_17.Tests
{
    [TestFixture]
    public class Tests
    {
        private static object[] TestValues =
        {
            new object[] {0, "zero", false},
            new object[] {11, "eleven", false},
            new object[] {20, "twenty", false},
            new object[] {47, "forty seven", false},
            new object[] {673, "six hundred seventy three", false},
            new object[] {8172, "eight thousand one hundred seventy two", false},
            new object[] {900000002, "nine hundred million two", false},
            new object[] {123456789, "one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine", false},
            new object[] {int.MaxValue, "two billion one hundred forty seven million four hundred eighty three thousand six hundred forty seven", false}, //2 147 483 647
            new object[] {4019, "four thousand nineteen", false},
            new object[] {28600, "twenty eight thousand six hundred", false },
            new object[] {0, "zero", true},
            new object[] {11, "eleven", true},
            new object[] {20, "twenty", true},
            new object[] {47, "forty seven", true},
            new object[] {673, "six hundred and seventy three", true},
            new object[] {8172, "eight thousand one hundred and seventy two", true},
            new object[] {900000002, "nine hundred million two", true},
            new object[] {123456789, "one hundred and twenty three million four hundred and fifty six thousand seven hundred and eighty nine", true},
            new object[] {int.MaxValue, "two billion one hundred and forty seven million four hundred and eighty three thousand six hundred and forty seven", true}, //2 147 483 647
            new object[] {4019, "four thousand nineteen", true},
            new object[] {28600, "twenty eight thousand six hundred", true },
            new object[] {342, "three hundred and forty two", true }
        };

        [Test, TestCaseSource("TestValues")]
        public void Euler17Test(int number, string expectedResult, bool includeAnd)
        {
            string result = IntToWordHelper.ConvertToWord(number, includeAnd);

            Assert.That(result, Is.EqualTo(expectedResult));
        }
    }
}

Comments


No comments yet, be the first to leave one.

Leave a Comment