5
\$\begingroup\$

I have a challenge, which is to create a JavaScript function that turns a given number into the string representation. For example:

console.log(inToEnglish(15)) should print fifteen

console.log(inToEnglish(101)) should print one hundred one

and so on...

This challenge covers Non-Negative, greater than zero Integer numbers.

I have accomplished this objective with the following code:

var b4Twenty = ["one", "two", "three", "four", "five", "six", "seven", "eight",
    "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen",
    "seventeen", "eighteen", "nineteen"
];

var b4Hundred = ["twenty", "thirty", "forty", "fifty", "sixty", "seventy",
    "eighty", "ninety"
];

function intToEnglish(n) {
    return translator(n).trim();
}

function translator(n) {
    if( n == 0)
        return "";   
    else if (n <= 19)
        return b4Twenty[n - 1] + " ";
    else if (n <= 99)
        return b4Hundred[Math.floor(n / 10 - 2)] + " " + translator(n % 10);
    else if (n <= 199)
        return "one hundred " + translator(n % 100);
    else if (n <= 999)
        return translator(Math.floor(n / 100)) + "hundred " + translator(n % 100);
    else if (n <= 1999)
        return "one thousand " + translator(n % 1000);
    else if (n <= 999999)
        return translator(Math.floor(n / 1000)) + "thousand " + translator(n % 1000);
    else if (n <= 1999999)
        return "one million " + translator(n % 1000000);
    else if (n <= 999999999)
        return translator(Math.floor(n / 1000000)) + "million " + translator(n % 1000000);
    else if (n <= 1999999999)
        return "one billion " + translator(n % 1000000000);
    else if (n <= 999999999999)
        return translator(Math.floor(n / 1000000000)) + "billion " + translator(n % 1000000000);
    else if (n <= 1999999999999)
        return "one trillion " + translator(n % 1000000000000);
    else if(n <= 999999999999999)
        return translator(Math.floor(n / 1000000000000)) + "trillion " + translator(n % 1000000000000);
    else if (n <= 1999999999999999)
        return "one quadrillion " + translator(n % 1000000000);
    else
        return translator(Math.floor(n / 1000000000000000)) + "quadrillion " + translator(n % 1000000000000000);
}

This is my recursive function to achieve the given goal. It works, and it takes 65ms to complete the battery of 50 tests.

However, I have some concerns regarding its performance:

  • Given that recursive functions are usually slower than iterative ones, is there a way to make this iterative?
  • Should I use a switch case, or is my if statement OK?
  • I keep using Math.floor to round the numbers and find the indexes. Perhaps there is a better solution out there without using this technique?
  • I have to wrap the translator function (who does all the work) into another function because I need to trim the final result of extra spaces. Is there a way I can avoid this?

I am open to suggestions on how to improve this. Thanks!

\$\endgroup\$
  • \$\begingroup\$ You might want to take a look at codereview.stackexchange.com/q/90349/10582 . \$\endgroup\$ – BCdotWEB Apr 5 '16 at 12:45
  • \$\begingroup\$ Not worthy of a full answer, but use proper words likebefore and not b4. Also I think that before isn't the best choice for what you want to express. Below is a better fit. \$\endgroup\$ – CodesInChaos Apr 7 '16 at 13:29
1
\$\begingroup\$

Interesting question! I came up with a premature function in my spare time. There are many TODOs should be done, but I think that its a good start.

First, instead of splitting logic into array, I would rather use an mapping object (dictionary).

const mapNumberToString = {
    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",
    30: "thirty",
    40: "forty",
    50: "fifty",
    60: "sixty",
    70: "seventy",
    80: "eighty",
    90: "ninety",
    100: "hundred",
    1000: "thousand",
    1000000: "million",
    1000000000: "billion",
    1000000000000: "trillion",
    1000000000000000: "quadrillion"
};

Then, my function goes like this...

const intToEnglish = (n) => {
    const arr = n.toString().split('').map(s => +s);
    let eng = "0";

    // under 20
    if (n <= 20 && n > 0) {
        eng = mapNumberToString[n];
    }
    // 21 ~ 99
    else if (arr.length < 3) {
        eng = [
            mapNumberToString[arr[0]*10],
            mapNumberToString[arr[1]]
        ].join(' ');
    }
    // 100 ~ 999
    else if (arr.length < 4) {
        eng = [
            mapNumberToString[arr[0]],
            mapNumberToString[100],
            intToEnglish(n % 100)
        ].join(' ');
    }
    // above 1000
    else {
        const exp = Math.pow(1000, Math.floor( (arr.length - 1) / 3));
        eng = [
            intToEnglish(Math.floor(n / exp)),
            mapNumberToString[exp],
            intToEnglish(n % exp)
        ].join(' ');
    }

    return eng.trim();
};

Taking advantages of array and recursive function, and also javascript weird undefined to make this magic happen.

1: one
5: five
10: ten
15: fifteen
20: twenty
25: twenty five
100: one hundred
105: one hundred five
115: one hundred fifteen
120: one hundred twenty
212: two hundred twelve
232: two hundred thirty two
999: nine hundred ninety nine
1000: one thousand
1234: one thousand two hundred thirty four
12345: twelve thousand three hundred forty five
123456: one hundred twenty three thousand four hundred fifty six
234100: two hundred thirty four thousand one hundred
1032001: one million thirty two thousand one
5000021: five million twenty one
810238903242: eight hundred ten billion two hundred thirty eight million nine hundred three thousand two hundred forty two

In this way, you can easily add up your mapping object without changing your code (quintillion, sextillion...). Plus, there are less if statement, which I think is easier to read, in my own opinion of course lol.

Again, there is so much to improve, and I currently don't have that much time :(.

\$\endgroup\$
3
\$\begingroup\$

intToEnglish(-1) returns "undefined", which might not be what you want. Same goes for fractional values, although I guess that case could be waved... if you only support integers. Even so, you might want to add a check. At the very least, add a comment - problem descriptions are kept separate from your code right now, and that makes it non-obvious that it doesn't support such things!

Negative numbers could be supported via prepending "minus", then translator(Math.abs(n)).

\$\endgroup\$
  • \$\begingroup\$ Apologies, it was a typo in my post ! The code is for integers (as the title indicates), but I will add the other parts of the information missing. The code is correct, thank you for noticing ! rep++ \$\endgroup\$ – Flame_Phoenix Apr 5 '16 at 13:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.