JavaScript numbers to Words

2019-01-01 01:06发布

I'm trying to convert numbers into english words, for example 1234 would become: "one thousand two hundred thirty four".

My Tactic goes like this:

  • Separate the digits to three and put them on Array (finlOutPut), from right to left.

  • Convert each group (each cell in the finlOutPut array) of three digits to a word (this what the triConvert function does). If all the three digits are zero convert them to "dontAddBigSuffix"

  • From Right to left, add thousand, million, billion, etc. If the finlOutPut cell equals "dontAddBigSufix" (because it was only zeroes), don't add the word and set the cell to " " (nothing).

It seems to work pretty well, but I've got some problems with numbers like 190000009, converted to: "one hundred ninety million". Somehow it "forgets" the last numbers when there are a few zeros.

What did I do wrong? Where is the bug? Why does it not work perfectly?

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>

<script type="text/javascript">
function update(){
    var bigNumArry = new Array('', ' thousand', ' million', ' billion', ' trillion', ' quadrillion', ' quintillion');

    var output = '';
    var numString =   document.getElementById('number').value;
    var finlOutPut = new Array();

    if (numString == '0') {
        document.getElementById('container').innerHTML = 'Zero';
        return;
    }

    if (numString == 0) {
        document.getElementById('container').innerHTML = 'messeg tell to enter numbers';
        return;
    }

    var i = numString.length;
    i = i - 1;

    //cut the number to grups of three digits and add them to the Arry
    while (numString.length > 3) {
        var triDig = new Array(3);
        triDig[2] = numString.charAt(numString.length - 1);
        triDig[1] = numString.charAt(numString.length - 2);
        triDig[0] = numString.charAt(numString.length - 3);

        var varToAdd = triDig[0] + triDig[1] + triDig[2];
        finlOutPut.push(varToAdd);
        i--;
        numString = numString.substring(0, numString.length - 3);
    }
    finlOutPut.push(numString);
    finlOutPut.reverse();

    //conver each grup of three digits to english word
    //if all digits are zero the triConvert
    //function return the string "dontAddBigSufix"
    for (j = 0; j < finlOutPut.length; j++) {
        finlOutPut[j] = triConvert(parseInt(finlOutPut[j]));
    }

    var bigScalCntr = 0; //this int mark the million billion trillion... Arry

    for (b = finlOutPut.length - 1; b >= 0; b--) {
        if (finlOutPut[b] != "dontAddBigSufix") {
            finlOutPut[b] = finlOutPut[b] + bigNumArry[bigScalCntr] + ' , ';
            bigScalCntr++;
        }
        else {
            //replace the string at finlOP[b] from "dontAddBigSufix" to empty String.
            finlOutPut[b] = ' ';
            bigScalCntr++; //advance the counter  
        }
    }

        //convert The output Arry to , more printable string 
        for(n = 0; n<finlOutPut.length; n++){
            output +=finlOutPut[n];
        }

    document.getElementById('container').innerHTML = output;//print the output
}

//simple function to convert from numbers to words from 1 to 999
function triConvert(num){
    var ones = new Array('', ' one', ' two', ' three', ' four', ' five', ' six', ' seven', ' eight', ' nine', ' ten', ' eleven', ' twelve', ' thirteen', ' fourteen', ' fifteen', ' sixteen', ' seventeen', ' eighteen', ' nineteen');
    var tens = new Array('', '', ' twenty', ' thirty', ' forty', ' fifty', ' sixty', ' seventy', ' eighty', ' ninety');
    var hundred = ' hundred';
    var output = '';
    var numString = num.toString();

    if (num == 0) {
        return 'dontAddBigSufix';
    }
    //the case of 10, 11, 12 ,13, .... 19 
    if (num < 20) {
        output = ones[num];
        return output;
    }

    //100 and more
    if (numString.length == 3) {
        output = ones[parseInt(numString.charAt(0))] + hundred;
        output += tens[parseInt(numString.charAt(1))];
        output += ones[parseInt(numString.charAt(2))];
        return output;
    }

    output += tens[parseInt(numString.charAt(0))];
    output += ones[parseInt(numString.charAt(1))];

    return output;
}   
</script>

</head>
<body>

<input type="text"
    id="number"
    size="70"
    onkeyup="update();"
    /*this code prevent non numeric letters*/ 
    onkeydown="return (event.ctrlKey || event.altKey 
                    || (47<event.keyCode && event.keyCode<58 && event.shiftKey==false) 
                    || (95<event.keyCode && event.keyCode<106)
                    || (event.keyCode==8) || (event.keyCode==9) 
                    || (event.keyCode>34 && event.keyCode<40) 
                    || (event.keyCode==46) )"/>
                    <br/>
<div id="container">Here The Numbers Printed</div>
</body>
</html>

21条回答
浮光初槿花落
2楼-- · 2019-01-01 01:26
function intToEnglish(number){

var NS = [
    {value: 1000000000000000000000, str: "sextillion"},
    {value: 1000000000000000000, str: "quintillion"},
    {value: 1000000000000000, str: "quadrillion"},
    {value: 1000000000000, str: "trillion"},
    {value: 1000000000, str: "billion"},
    {value: 1000000, str: "million"},
    {value: 1000, str: "thousand"},
    {value: 100, str: "hundred"},
    {value: 90, str: "ninety"},
    {value: 80, str: "eighty"},
    {value: 70, str: "seventy"},
    {value: 60, str: "sixty"},
    {value: 50, str: "fifty"},
    {value: 40, str: "forty"},
    {value: 30, str: "thirty"},
    {value: 20, str: "twenty"},
    {value: 19, str: "nineteen"},
    {value: 18, str: "eighteen"},
    {value: 17, str: "seventeen"},
    {value: 16, str: "sixteen"},
    {value: 15, str: "fifteen"},
    {value: 14, str: "fourteen"},
    {value: 13, str: "thirteen"},
    {value: 12, str: "twelve"},
    {value: 11, str: "eleven"},
    {value: 10, str: "ten"},
    {value: 9, str: "nine"},
    {value: 8, str: "eight"},
    {value: 7, str: "seven"},
    {value: 6, str: "six"},
    {value: 5, str: "five"},
    {value: 4, str: "four"},
    {value: 3, str: "three"},
    {value: 2, str: "two"},
    {value: 1, str: "one"}
  ];

  var result = '';
  for (var n of NS) {
    if(number>=n.value){
      if(number<=20){
        result += n.str;
        number -= n.value;
        if(number>0) result += ' ';
      }else{
        var t =  Math.floor(number / n.value);
        var d = number % n.value;
        if(d>0){
          return intToEnglish(t) + ' ' + n.str +' ' + intToEnglish(d);
        }else{
          return intToEnglish(t) + ' ' + n.str;
        }

      }
    }
  }
  return result;
}
查看更多
宁负流年不负卿
3楼-- · 2019-01-01 01:27

Here is another version from me with some unit tests.

Don't use it with numbers larger than Number.MAX_SAFE_INTEGER.

describe("English Numerals Converter", function () {
  assertNumeral(0, "zero");
  assertNumeral(1, "one");
  assertNumeral(2, "two");
  assertNumeral(3, "three");
  assertNumeral(4, "four");
  assertNumeral(5, "five");
  assertNumeral(6, "six");
  assertNumeral(7, "seven");
  assertNumeral(8, "eight");
  assertNumeral(9, "nine");
  assertNumeral(10, "ten");
  assertNumeral(11, "eleven");
  assertNumeral(12, "twelve");
  assertNumeral(13, "thirteen");
  assertNumeral(14, "fourteen");
  assertNumeral(15, "fifteen");
  assertNumeral(16, "sixteen");
  assertNumeral(17, "seventeen");
  assertNumeral(18, "eighteen");
  assertNumeral(19, "nineteen");
  assertNumeral(20, "twenty");
  assertNumeral(21, "twenty-one");
  assertNumeral(22, "twenty-two");
  assertNumeral(23, "twenty-three");
  assertNumeral(30, "thirty");
  assertNumeral(37, "thirty-seven");
  assertNumeral(40, "forty");
  assertNumeral(50, "fifty");
  assertNumeral(60, "sixty");
  assertNumeral(70, "seventy");
  assertNumeral(80, "eighty");
  assertNumeral(90, "ninety");
  assertNumeral(99, "ninety-nine");
  assertNumeral(100, "one hundred");
  assertNumeral(101, "one hundred and one");
  assertNumeral(102, "one hundred and two");
  assertNumeral(110, "one hundred and ten");
  assertNumeral(120, "one hundred and twenty");
  assertNumeral(121, "one hundred and twenty-one");
  assertNumeral(199, "one hundred and ninety-nine");
  assertNumeral(200, "two hundred");
  assertNumeral(999, "nine hundred and ninety-nine");
  assertNumeral(1000, "one thousand");
  assertNumeral(1001, "one thousand and one");
  assertNumeral(1011, "one thousand and eleven");
  assertNumeral(1111, "one thousand and one hundred and eleven");
  assertNumeral(9999, "nine thousand and nine hundred and ninety-nine");
  assertNumeral(10000, "ten thousand");
  assertNumeral(20000, "twenty thousand");
  assertNumeral(21000, "twenty-one thousand");
  assertNumeral(90000, "ninety thousand");
  assertNumeral(90001, "ninety thousand and one");
  assertNumeral(90100, "ninety thousand and one hundred");
  assertNumeral(90901, "ninety thousand and nine hundred and one");
  assertNumeral(90991, "ninety thousand and nine hundred and ninety-one");
  assertNumeral(90999, "ninety thousand and nine hundred and ninety-nine");
  assertNumeral(91000, "ninety-one thousand");
  assertNumeral(99999, "ninety-nine thousand and nine hundred and ninety-nine");
  assertNumeral(100000, "one hundred thousand");
  assertNumeral(999000, "nine hundred and ninety-nine thousand");
  assertNumeral(1000000, "one million");
  assertNumeral(10000000, "ten million");
  assertNumeral(100000000, "one hundred million");
  assertNumeral(1000000000, "one billion");
  assertNumeral(1000000000000, "one trillion");
  assertNumeral(1000000000000000, "one quadrillion");
  assertNumeral(1000000000000000000, "one quintillion");
  assertNumeral(1000000000000000000000, "one sextillion");

  assertNumeral(-1, "minus one");
  assertNumeral(-999, "minus nine hundred and ninety-nine");

  function assertNumeral(number, numeral) {
    it(number + " is " + numeral, function () {
      expect(convert(number)).toBe(numeral);
    });
  }
});


function convert(n) {
  let NUMERALS = [
    {value: 1000000000000000000000, str: "sextillion"},
    {value: 1000000000000000000, str: "quintillion"},
    {value: 1000000000000000, str: "quadrillion"},
    {value: 1000000000000, str: "trillion"},
    {value: 1000000000, str: "billion"},
    {value: 1000000, str: "million"},
    {value: 1000, str: "thousand"},
    {value: 100, str: "hundred"},
    {value: 90, str: "ninety"},
    {value: 80, str: "eighty"},
    {value: 70, str: "seventy"},
    {value: 60, str: "sixty"},
    {value: 50, str: "fifty"},
    {value: 40, str: "forty"},
    {value: 30, str: "thirty"},
    {value: 20, str: "twenty"},
    {value: 19, str: "nineteen"},
    {value: 18, str: "eighteen"},
    {value: 17, str: "seventeen"},
    {value: 16, str: "sixteen"},
    {value: 15, str: "fifteen"},
    {value: 14, str: "fourteen"},
    {value: 13, str: "thirteen"},
    {value: 12, str: "twelve"},
    {value: 11, str: "eleven"},
    {value: 10, str: "ten"},
    {value: 9, str: "nine"},
    {value: 8, str: "eight"},
    {value: 7, str: "seven"},
    {value: 6, str: "six"},
    {value: 5, str: "five"},
    {value: 4, str: "four"},
    {value: 3, str: "three"},
    {value: 2, str: "two"},
    {value: 1, str: "one"}
  ];

  if (n < 0) {
    return "minus " + convert(-n);
  } else if (n === 0) {
    return "zero";
  } else {
    let result = "";
    for (let numeral of NUMERALS) {
      if (n >= numeral.value) {
        if (n < 100) {
          result += numeral.str;
          n -= numeral.value;
          if (n > 0) result += "-";
        } else {
          let times = Math.floor(n / numeral.value);
          result += convert(times) + " " + numeral.str;
          n -= numeral.value * times;
          if (n > 0) result += " and ";
        }
      }
    }
    return result;
  }
}
查看更多
时光乱了年华
4楼-- · 2019-01-01 01:29

Your problem is already solved but I am posting another way of doing it just for reference.

The code was written to be tested on node.js, but the functions should work fine when called within the browser. Also, this only handles the range [0,1000000], but can be easily adapted for bigger ranges.

#! /usr/bin/env node

var sys=require('sys');

// actual  conversion code starts here

var ones=['','one','two','three','four','five','six','seven','eight','nine'];
var tens=['','','twenty','thirty','forty','fifty','sixty','seventy','eighty','ninety'];
var teens=['ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen'];


function convert_millions(num){
    if (num>=1000000){
        return convert_millions(Math.floor(num/1000000))+" million "+convert_thousands(num%1000000);
    }
    else {
        return convert_thousands(num);
    }
}

function convert_thousands(num){
    if (num>=1000){
        return convert_hundreds(Math.floor(num/1000))+" thousand "+convert_hundreds(num%1000);
    }
    else{
        return convert_hundreds(num);
    }
}

function convert_hundreds(num){
    if (num>99){
        return ones[Math.floor(num/100)]+" hundred "+convert_tens(num%100);
    }
    else{
        return convert_tens(num);
    }
}

function convert_tens(num){
    if (num<10) return ones[num];
    else if (num>=10 && num<20) return teens[num-10];
    else{
        return tens[Math.floor(num/10)]+" "+ones[num%10];
    }
}

function convert(num){
    if (num==0) return "zero";
    else return convert_millions(num);
}

//end of conversion code

//testing code begins here

function main(){
    var cases=[0,1,2,7,10,11,12,13,15,19,20,21,25,29,30,35,50,55,69,70,99,100,101,119,510,900,1000,5001,5019,5555,10000,11000,100000,199001,1000000,1111111,190000009];
    for (var i=0;i<cases.length;i++ ){
        sys.puts(cases[i]+": "+convert(cases[i]));
    }
}

main();
查看更多
与君花间醉酒
5楼-- · 2019-01-01 01:31

JavaScript is parsing the group of 3 numbers as an octal number when there's a leading zero digit. When the group of three digits is all zeros, the result is the same whether the base is octal or decimal.

But when you give JavaScript '009' (or '008'), that's an invalid octal number, so you get zero back.

If you had gone through the whole set of numbers from 190,000,001 to 190,000,010 you'd hav seen JavaScript skip '...,008' and '...,009' but emit 'eight' for '...,010'. That's the 'Eureka!' moment.

Change:

for (j = 0; j < finlOutPut.length; j++) {
    finlOutPut[j] = triConvert(parseInt(finlOutPut[j]));
}

to

for (j = 0; j < finlOutPut.length; j++) {
    finlOutPut[j] = triConvert(parseInt(finlOutPut[j],10));
}

Code also kept on adding commas after every non-zero group, so I played with it and found the right spot to add the comma.

Old:

for (b = finlOutPut.length - 1; b >= 0; b--) {
    if (finlOutPut[b] != "dontAddBigSufix") {
        finlOutPut[b] = finlOutPut[b] + bigNumArry[bigScalCntr] + ' , ';
        bigScalCntr++;
    }
    else {
        //replace the string at finlOP[b] from "dontAddBigSufix" to empty String.
        finlOutPut[b] = ' ';
        bigScalCntr++; //advance the counter  
    }
}

    //convert The output Arry to , more printable string 
    for(n = 0; n<finlOutPut.length; n++){
        output +=finlOutPut[n];
    }

New:

for (b = finlOutPut.length - 1; b >= 0; b--) {
    if (finlOutPut[b] != "dontAddBigSufix") {
        finlOutPut[b] = finlOutPut[b] + bigNumArry[bigScalCntr]; // <<<
        bigScalCntr++;
    }
    else {
        //replace the string at finlOP[b] from "dontAddBigSufix" to empty String.
        finlOutPut[b] = ' ';
        bigScalCntr++; //advance the counter  
    }
}

    //convert The output Arry to , more printable string 
    var nonzero = false; // <<<
    for(n = 0; n<finlOutPut.length; n++){
        if (finlOutPut[n] != ' ') { // <<<
            if (nonzero) output += ' , '; // <<<
            nonzero = true; // <<<
        } // <<<
        output +=finlOutPut[n];
    }
查看更多
姐姐魅力值爆表
6楼-- · 2019-01-01 01:31

This is a simple ES6+ number to words function. You can simply add 'illions' array to extend digits. American English version. (no 'and' before the end)

// generic number to words

let digits  = ['','one','two','three','four', 'five','six','seven','eight','nine','ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen'];
let ties    = ['', '', 'twenty','thirty','forty','fifty', 'sixty','seventy','eighty','ninety'];
let illions = ['', 'thousand', 'million', 'billion', 'trillion'].reverse()

let join = (a, s) => a.filter(v => v).join(s || ' ')

let tens = s => 
    digits[s] || 
    join([ties[s[0]], digits[s[1]]], '-') // 21 -> twenty-one

let hundreds = s => 
    join(
        (s[0] !== '0' ? [digits[s[0]], 'hundred'] : [])
            .concat( tens(s.substr(1,2)) )  )

let re = '^' + '(\\d{3})'.repeat(illions.length) + '$'

let numberToWords = n => 
    // to filter non number or '', null, undefined, false, NaN
    isNaN(Number(n)) || !n && n !== 0 
        ? 'not a number'
        : Number(n) === 0 
            ? 'zero'  
            : Number(n) >= 10 ** (illions.length * 3)
                ? 'too big'
                : String(n)
                    .padStart(illions.length * 3, '0')
                    .match(new RegExp(re))
                    .slice(1, illions.length + 1)
                    .reduce( (a, v, i) => v === '000' ? a : join([a, hundreds(v), illions[i]]), '')


// just for this question.

let update = () => {
    let value = document.getElementById('number').value
    document.getElementById('container').innerHTML = numberToWords(value)
}
查看更多
素衣白纱
7楼-- · 2019-01-01 01:34

Try this,convert number to words

function convert(number) {

    if (number < 0) {

        console.log("Number Must be greater than zero = " + number);
        return "";
    }
    if (number > 100000000000000000000) {
        console.log("Number is out of range = " + number);
        return "";
    }
    if (!is_numeric(number)) {
        console.log("Not a number = " + number);
        return "";
    }

    var quintillion = Math.floor(number / 1000000000000000000); /* quintillion */
    number -= quintillion * 1000000000000000000;
    var quar = Math.floor(number / 1000000000000000); /* quadrillion */
    number -= quar * 1000000000000000;
    var trin = Math.floor(number / 1000000000000); /* trillion */
    number -= trin * 1000000000000;
    var Gn = Math.floor(number / 1000000000); /* billion */
    number -= Gn * 1000000000;
    var million = Math.floor(number / 1000000); /* million */
    number -= million * 1000000;
    var Hn = Math.floor(number / 1000); /* thousand */
    number -= Hn * 1000;
    var Dn = Math.floor(number / 100); /* Tens (deca) */
    number = number % 100; /* Ones */
    var tn = Math.floor(number / 10);
    var one = Math.floor(number % 10);
    var res = "";

    if (quintillion > 0) {
        res += (convert_number(quintillion) + " quintillion");
    }
    if (quar > 0) {
        res += (convert_number(quar) + " quadrillion");
    }
    if (trin > 0) {
        res += (convert_number(trin) + " trillion");
    }
    if (Gn > 0) {
        res += (convert_number(Gn) + " billion");
    }
    if (million > 0) {
        res += (((res == "") ? "" : " ") + convert_number(million) + " million");
    }
    if (Hn > 0) {
        res += (((res == "") ? "" : " ") + convert_number(Hn) + " Thousand");
    }

    if (Dn) {
        res += (((res == "") ? "" : " ") + convert_number(Dn) + " hundred");
    }


    var ones = Array("", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eightteen", "Nineteen");
    var tens = Array("", "", "Twenty", "Thirty", "Fourty", "Fifty", "Sixty", "Seventy", "Eigthy", "Ninety");

    if (tn > 0 || one > 0) {
        if (!(res == "")) {
            res += " and ";
        }
        if (tn < 2) {
            res += ones[tn * 10 + one];
        } else {

            res += tens[tn];
            if (one > 0) {
                res += ("-" + ones[one]);
            }
        }
    }

    if (res == "") {
        console.log("Empty = " + number);
        res = "";
    }
    return res;
}
function is_numeric(mixed_var) {
    return (typeof mixed_var === 'number' || typeof mixed_var === 'string') && mixed_var !== '' && !isNaN(mixed_var);
}
查看更多
登录 后发表回答