HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Spelling a number in English

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
spellingnumberenglish

Problem

My Implementation:

```
string NumberToEnglish(long n)
{
StringBuilder builder = new StringBuilder();
var trillion = 1000000000000;
var billion = 1000000000;
var million = 1000000;
var thousand = 1000;
var hundred = 100;
while(n != 0){
if(n >= trillion){
builder.AppendFormat("{0} Trillion ",NumberToEnglish(n / trillion));
n -= (n / trillion) * trillion;
}
else if(n >= billion){
builder.AppendFormat("{0} Billion ",NumberToEnglish(n / billion));
n -= (n / billion) * billion;
}
else if(n >= million){
builder.AppendFormat("{0} Million ",NumberToEnglish(n / million));
n -= (n / million) * million;
}
else if(n >= thousand){
builder.AppendFormat("{0} Thousand ",NumberToEnglish(n / thousand));
n -= (n / thousand) * thousand;
}
else if(n >= hundred){
builder.AppendFormat("{0} Hundred ",NumberToEnglish(n / hundred));
n -= (n / hundred) * hundred;
}else if(n >= 20){
builder.AppendFormat(numerals[(n / 10) * 10] + " ");
n -= (n / 10) * 10;
}else{
builder.Append(numerals[n]);
n -= n;
}
}
return builder.ToString();
}

Dictionary numerals = new Dictionary(){
{ 1, "One" },
{ 2, "Two" },
{ 3, "Three" },
{ 4, "Four"},
{ 5, "Five"},
{ 6, "Six"} ,
{ 7, "Seven"},
{ 8, "Eight"},
{ 9, "Nine"},
{ 10, "Ten" },
{ 11, "Eleven"},
{ 12, "Twelve"},
{ 13, "Thirteen"},
{ 14, "Fourteen"},
{ 15, "Fifteen" },
{ 16, "Sixteen" },
{ 17, "Seventeen"},
{ 18, "Eighteen"},
{ 19, "Nineteen"},
{ 20, "Twenty"

Solution

I took an object-oriented approach here, and below you can find what I ended up with. Why do I think it's better? Well, basically, because you should need the ability to control how each one of the number parts is processed: whether it's plural, whether it should be changed according to previous (or following) number parts. The latter one does not really apply to english, but it applies to some other languages. If you did all that in one function, you would end up with having a bunch of code, which performs different kinds of tasks (1. retrieve string value 2. apply plural/singular 3. apply gender 4. Apply "forth"/"four" formatting rules). But taking the object oriented approach you now have a standalone part which does thing 1. When you need to think about 2-4, you can integrate other parsers into your code.

// This is what your initial function looks like
public static string NumberToEnglish(long n) {
    StringBuilder builder = new StringBuilder();
    var steps = new INumberPart[] {
        new SimpleNumberPart{Step = 1000000000000}, //trillion
        new SimpleNumberPart{Step = 1000000000}, //billion
        new SimpleNumberPart{Step = 1000000}, //million
        new SimpleNumberPart{Step = 1000}, //thousand
        new SimpleNumberPart{Step = 100}, //hundred
        new DecimalNumberPart(),
        new TeenNumberPart()
    };
    while (n != 0) {
        var firstAvailable = steps.First(s => s.CanBeFormatted(n));
        n = firstAvailable.Format(n, builder);
    }
    return builder.ToString();
}

public interface INumberPart {
    bool CanBeFormatted(long n);
    long Format(long n, StringBuilder s);
}

public class SimpleNumberPart : INumberPart {
    public long Step { get; set; }

    public bool CanBeFormatted(long n) {
        return n >= Step;
    }

    public long Format(long n, StringBuilder s) {
        return ProcessNum(n, Step, s);
    }

    private static long ProcessNum(long n, long step, StringBuilder builder) {
        var stepValue = n / step;
        builder.AppendFormat("{0}", NumberToEnglish(stepValue));
        builder.AppendFormat("{0} ", numerals[step]);
        return n - stepValue * step;
    }
}

public class DecimalNumberPart : INumberPart {
    public bool CanBeFormatted(long n) {
        return n >= 20;
    }

    public long Format(long n, StringBuilder s) {
        s.AppendFormat("{0} ", numerals[(n / 10) * 10]);
        return n - (n/10)*10;
    }
}

public class TeenNumberPart : INumberPart {
    public bool CanBeFormatted(long n) {
        return 0 <= n && n < 20;
    }

    public long Format(long n, StringBuilder s) {
        s.AppendFormat("{0} ", numerals[n]);
        return 0;
    }
}

Code Snippets

// This is what your initial function looks like
public static string NumberToEnglish(long n) {
    StringBuilder builder = new StringBuilder();
    var steps = new INumberPart[] {
        new SimpleNumberPart{Step = 1000000000000}, //trillion
        new SimpleNumberPart{Step = 1000000000}, //billion
        new SimpleNumberPart{Step = 1000000}, //million
        new SimpleNumberPart{Step = 1000}, //thousand
        new SimpleNumberPart{Step = 100}, //hundred
        new DecimalNumberPart(),
        new TeenNumberPart()
    };
    while (n != 0) {
        var firstAvailable = steps.First(s => s.CanBeFormatted(n));
        n = firstAvailable.Format(n, builder);
    }
    return builder.ToString();
}

public interface INumberPart {
    bool CanBeFormatted(long n);
    long Format(long n, StringBuilder s);
}

public class SimpleNumberPart : INumberPart {
    public long Step { get; set; }

    public bool CanBeFormatted(long n) {
        return n >= Step;
    }

    public long Format(long n, StringBuilder s) {
        return ProcessNum(n, Step, s);
    }

    private static long ProcessNum(long n, long step, StringBuilder builder) {
        var stepValue = n / step;
        builder.AppendFormat("{0}", NumberToEnglish(stepValue));
        builder.AppendFormat("{0} ", numerals[step]);
        return n - stepValue * step;
    }
}

public class DecimalNumberPart : INumberPart {
    public bool CanBeFormatted(long n) {
        return n >= 20;
    }

    public long Format(long n, StringBuilder s) {
        s.AppendFormat("{0} ", numerals[(n / 10) * 10]);
        return n - (n/10)*10;
    }
}

public class TeenNumberPart : INumberPart {
    public bool CanBeFormatted(long n) {
        return 0 <= n && n < 20;
    }

    public long Format(long n, StringBuilder s) {
        s.AppendFormat("{0} ", numerals[n]);
        return 0;
    }
}

Context

StackExchange Code Review Q#67793, answer score: 6

Revisions (0)

No revisions yet.