Convert digits into words with JavaScript

后端 未结 27 1247
我寻月下人不归
我寻月下人不归 2020-11-22 15:08

I am making a code which converts the given amount into words, heres is what I have got after googling. But I think its a little lengthy code to achieve a simple task. Two R

相关标签:
27条回答
  • 2020-11-22 15:41

    You might want to try it recursive. It works for numbers between 0 and 999999. Keep in mind that (~~) does the same as Math.floor

    var num = "zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen".split(" ");
    var tens = "twenty thirty forty fifty sixty seventy eighty ninety".split(" ");
    
    function number2words(n){
        if (n < 20) return num[n];
        var digit = n%10;
        if (n < 100) return tens[~~(n/10)-2] + (digit? "-" + num[digit]: "");
        if (n < 1000) return num[~~(n/100)] +" hundred" + (n%100 == 0? "": " " + number2words(n%100));
        return number2words(~~(n/1000)) + " thousand" + (n%1000 != 0? " " + number2words(n%1000): "");
    }

    0 讨论(0)
  • 2020-11-22 15:44
    var inWords = function(totalRent){
    //console.log(totalRent);
    var a = ['','one ','two ','three ','four ', 'five ','six ','seven ','eight ','nine ','ten ','eleven ','twelve ','thirteen ','fourteen ','fifteen ','sixteen ','seventeen ','eighteen ','nineteen '];
    var b = ['', '', 'twenty','thirty','forty','fifty', 'sixty','seventy','eighty','ninety'];
    var number = parseFloat(totalRent).toFixed(2).split(".");
    var num = parseInt(number[0]);
    var digit = parseInt(number[1]);
    //console.log(num);
    if ((num.toString()).length > 9)  return 'overflow';
    var n = ('000000000' + num).substr(-9).match(/^(\d{2})(\d{2})(\d{2})(\d{1})(\d{2})$/);
    var d = ('00' + digit).substr(-2).match(/^(\d{2})$/);;
    if (!n) return; var str = '';
    str += (n[1] != 0) ? (a[Number(n[1])] || b[n[1][0]] + ' ' + a[n[1][1]]) + 'crore ' : '';
    str += (n[2] != 0) ? (a[Number(n[2])] || b[n[2][0]] + ' ' + a[n[2][1]]) + 'lakh ' : '';
    str += (n[3] != 0) ? (a[Number(n[3])] || b[n[3][0]] + ' ' + a[n[3][1]]) + 'thousand ' : '';
    str += (n[4] != 0) ? (a[Number(n[4])] || b[n[4][0]] + ' ' + a[n[4][1]]) + 'hundred ' : '';
    str += (n[5] != 0) ? (a[Number(n[5])] || b[n[5][0]] + ' ' + a[n[5][1]]) + 'Rupee ' : '';
    str += (d[1] != 0) ? ((str != '' ) ? "and " : '') + (a[Number(d[1])] || b[d[1][0]] + ' ' + a[d[1][1]]) + 'Paise ' : 'Only!';
    console.log(str);
    return str;
    }
    

    This is modified code supports for Indian Rupee with 2 decimal place.

    0 讨论(0)
  • 2020-11-22 15:45

    I spent a while developing a better solution to this. It can handle very big numbers but once they get over 16 digits you have pass the number in as a string. Something about the limit of JavaScript numbers.

        function numberToEnglish( n ) {
            
            var string = n.toString(), units, tens, scales, start, end, chunks, chunksLen, chunk, ints, i, word, words, and = 'and';
    
            /* Remove spaces and commas */
            string = string.replace(/[, ]/g,"");
    
            /* Is number zero? */
            if( parseInt( string ) === 0 ) {
                return 'zero';
            }
            
            /* Array of units as words */
            units = [ '', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen' ];
            
            /* Array of tens as words */
            tens = [ '', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety' ];
            
            /* Array of scales as words */
            scales = [ '', 'thousand', 'million', 'billion', 'trillion', 'quadrillion', 'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion', 'decillion', 'undecillion', 'duodecillion', 'tredecillion', 'quatttuor-decillion', 'quindecillion', 'sexdecillion', 'septen-decillion', 'octodecillion', 'novemdecillion', 'vigintillion', 'centillion' ];
            
            /* Split user argument into 3 digit chunks from right to left */
            start = string.length;
            chunks = [];
            while( start > 0 ) {
                end = start;
                chunks.push( string.slice( ( start = Math.max( 0, start - 3 ) ), end ) );
            }
            
            /* Check if function has enough scale words to be able to stringify the user argument */
            chunksLen = chunks.length;
            if( chunksLen > scales.length ) {
                return '';
            }
            
            /* Stringify each integer in each chunk */
            words = [];
            for( i = 0; i < chunksLen; i++ ) {
                
                chunk = parseInt( chunks[i] );
                
                if( chunk ) {
                    
                    /* Split chunk into array of individual integers */
                    ints = chunks[i].split( '' ).reverse().map( parseFloat );
                
                    /* If tens integer is 1, i.e. 10, then add 10 to units integer */
                    if( ints[1] === 1 ) {
                        ints[0] += 10;
                    }
                    
                    /* Add scale word if chunk is not zero and array item exists */
                    if( ( word = scales[i] ) ) {
                        words.push( word );
                    }
                    
                    /* Add unit word if array item exists */
                    if( ( word = units[ ints[0] ] ) ) {
                        words.push( word );
                    }
                    
                    /* Add tens word if array item exists */
                    if( ( word = tens[ ints[1] ] ) ) {
                        words.push( word );
                    }
                    
                    /* Add 'and' string after units or tens integer if: */
                    if( ints[0] || ints[1] ) {
                        
                        /* Chunk has a hundreds integer or chunk is the first of multiple chunks */
                        if( ints[2] || ! i && chunksLen ) {
                            words.push( and );
                        }
                    
                    }
                    
                    /* Add hundreds word if array item exists */
                    if( ( word = units[ ints[2] ] ) ) {
                        words.push( word + ' hundred' );
                    }
                    
                }
                
            }
            
            return words.reverse().join( ' ' );
            
        }
    
    
    // - - - - - Tests - - - - - -
    function test(v) {
      var sep = ('string'==typeof v)?'"':'';
      console.log("numberToEnglish("+sep + v.toString() + sep+") = "+numberToEnglish(v));
    }
    test(2);
    test(721);
    test(13463);
    test(1000001);
    test("21,683,200,000,621,384");

    0 讨论(0)
  • 2020-11-22 15:46

    "Deceptively simple task." – Potatoswatter

    Indeed. There's many little devils hanging out in the details of this problem. It was very fun to solve tho.

    EDIT: This update takes a much more compositional approach. Previously there was one big function which wrapped a couple other proprietary functions. Instead, this time we define generic reusable functions which could be used for many varieties of tasks. More about those after we take a look at numToWords itself …

    // numToWords :: (Number a, String a) => a -> String
    let numToWords = n => {
      let a = [
        '', 'one', 'two', 'three', 'four',
        'five', 'six', 'seven', 'eight', 'nine',
        'ten', 'eleven', 'twelve', 'thirteen', 'fourteen',
        'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'
      ];
      let b = [
        '', '', 'twenty', 'thirty', 'forty',
        'fifty', 'sixty', 'seventy', 'eighty', 'ninety'
      ];
      let g = [
        '', 'thousand', 'million', 'billion', 'trillion', 'quadrillion',
        'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion'
      ];
      // this part is really nasty still
      // it might edit this again later to show how Monoids could fix this up
      let makeGroup = ([ones,tens,huns]) => {
        return [
          num(huns) === 0 ? '' : a[huns] + ' hundred ',
          num(ones) === 0 ? b[tens] : b[tens] && b[tens] + '-' || '',
          a[tens+ones] || a[ones]
        ].join('');
      };
      // "thousands" constructor; no real good names for this, i guess
      let thousand = (group,i) => group === '' ? group : `${group} ${g[i]}`;
      // execute !
      if (typeof n === 'number') return numToWords(String(n));
      if (n === '0')             return 'zero';
      return comp (chunk(3)) (reverse) (arr(n))
        .map(makeGroup)
        .map(thousand)
        .filter(comp(not)(isEmpty))
        .reverse()
        .join(' ');
    };
    

    Here are the dependencies:

    You'll notice these require next to no documentation because their intents are immediately clear. chunk might be the only one that takes a moment to digest, but it's really not too bad. Plus the function name gives us a pretty good indication what it does, and it's probably a function we've encountered before.

    const arr = x => Array.from(x);
    const num = x => Number(x) || 0;
    const str = x => String(x);
    const isEmpty = xs => xs.length === 0;
    const take = n => xs => xs.slice(0,n);
    const drop = n => xs => xs.slice(n);
    const reverse = xs => xs.slice(0).reverse();
    const comp = f => g => x => f (g (x));
    const not = x => !x;
    const chunk = n => xs =>
      isEmpty(xs) ? [] : [take(n)(xs), ...chunk (n) (drop (n) (xs))];
    

    "So these make it better?"

    Look at how the code has cleaned up significantly

    // NEW CODE (truncated)
    return comp (chunk(3)) (reverse) (arr(n))
        .map(makeGroup)
        .map(thousand)
        .filter(comp(not)(isEmpty))
        .reverse()
        .join(' ');
    
    // OLD CODE (truncated)
    let grp = n => ('000' + n).substr(-3);
    let rem = n => n.substr(0, n.length - 3);
    let cons = xs => x => g => x ? [x, g && ' ' + g || '', ' ', xs].join('') : xs;
    let iter = str => i => x => r => {
      if (x === '000' && r.length === 0) return str;
      return iter(cons(str)(fmt(x))(g[i]))
                 (i+1)
                 (grp(r))
                 (rem(r));
    };
    return iter('')(0)(grp(String(n)))(rem(String(n)));
    

    Most importantly, the utility functions we added in the new code can be used other places in your app. This means that, as a side effect of implementing numToWords in this way, we get the other functions for free. Bonus soda !

    Some tests

    console.log(numToWords(11009));
    //=> eleven thousand nine
    
    console.log(numToWords(10000001));
    //=> ten million one 
    
    console.log(numToWords(987));
    //=> nine hundred eighty-seven
    
    console.log(numToWords(1015));
    //=> one thousand fifteen
    
    console.log(numToWords(55111222333));
    //=> fifty-five billion one hundred eleven million two hundred 
    //   twenty-two thousand three hundred thirty-three
    
    console.log(numToWords("999999999999999999999991"));
    //=> nine hundred ninety-nine sextillion nine hundred ninety-nine
    //   quintillion nine hundred ninety-nine quadrillion nine hundred
    //   ninety-nine trillion nine hundred ninety-nine billion nine
    //   hundred ninety-nine million nine hundred ninety-nine thousand
    //   nine hundred ninety-one
    
    console.log(numToWords(6000753512));
    //=> six billion seven hundred fifty-three thousand five hundred
    //   twelve 
    

    Runnable demo

    const arr = x => Array.from(x);
    const num = x => Number(x) || 0;
    const str = x => String(x);
    const isEmpty = xs => xs.length === 0;
    const take = n => xs => xs.slice(0,n);
    const drop = n => xs => xs.slice(n);
    const reverse = xs => xs.slice(0).reverse();
    const comp = f => g => x => f (g (x));
    const not = x => !x;
    const chunk = n => xs =>
      isEmpty(xs) ? [] : [take(n)(xs), ...chunk (n) (drop (n) (xs))];
    
    // numToWords :: (Number a, String a) => a -> String
    let numToWords = n => {
      
      let a = [
        '', 'one', 'two', 'three', 'four',
        'five', 'six', 'seven', 'eight', 'nine',
        'ten', 'eleven', 'twelve', 'thirteen', 'fourteen',
        'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'
      ];
      
      let b = [
        '', '', 'twenty', 'thirty', 'forty',
        'fifty', 'sixty', 'seventy', 'eighty', 'ninety'
      ];
      
      let g = [
        '', 'thousand', 'million', 'billion', 'trillion', 'quadrillion',
        'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion'
      ];
      
      // this part is really nasty still
      // it might edit this again later to show how Monoids could fix this up
      let makeGroup = ([ones,tens,huns]) => {
        return [
          num(huns) === 0 ? '' : a[huns] + ' hundred ',
          num(ones) === 0 ? b[tens] : b[tens] && b[tens] + '-' || '',
          a[tens+ones] || a[ones]
        ].join('');
      };
      
      let thousand = (group,i) => group === '' ? group : `${group} ${g[i]}`;
      
      if (typeof n === 'number')
        return numToWords(String(n));
      else if (n === '0')
        return 'zero';
      else
        return comp (chunk(3)) (reverse) (arr(n))
          .map(makeGroup)
          .map(thousand)
          .filter(comp(not)(isEmpty))
          .reverse()
          .join(' ');
    };
    
    
    console.log(numToWords(11009));
    //=> eleven thousand nine
    
    console.log(numToWords(10000001));
    //=> ten million one 
    
    console.log(numToWords(987));
    //=> nine hundred eighty-seven
    
    console.log(numToWords(1015));
    //=> one thousand fifteen
    
    console.log(numToWords(55111222333));
    //=> fifty-five billion one hundred eleven million two hundred 
    //   twenty-two thousand three hundred thirty-three
    
    console.log(numToWords("999999999999999999999991"));
    //=> nine hundred ninety-nine sextillion nine hundred ninety-nine
    //   quintillion nine hundred ninety-nine quadrillion nine hundred
    //   ninety-nine trillion nine hundred ninety-nine billion nine
    //   hundred ninety-nine million nine hundred ninety-nine thousand
    //   nine hundred ninety-one
    
    console.log(numToWords(6000753512));
    //=> six billion seven hundred fifty-three thousand five hundred
    //   twelve


    You can transpile the code using babel.js if you want to see the ES5 variant

    0 讨论(0)
  • 2020-11-22 15:46

     <html>
    
    <head>
    
        <title>HTML - Convert numbers to words using JavaScript</title>
    
        <script  type="text/javascript">
        	function onlyNumbers(evt) {
        var e = event || evt; // For trans-browser compatibility
        var charCode = e.which || e.keyCode;
    
        if (charCode > 31 && (charCode < 48 || charCode > 57))
            return false;
        return true;
    }
    
    function NumToWord(inputNumber, outputControl) {
        var str = new String(inputNumber)
        var splt = str.split("");
        var rev = splt.reverse();
        var once = ['Zero', ' One', ' Two', ' Three', ' Four', ' Five', ' Six', ' Seven', ' Eight', ' Nine'];
        var twos = ['Ten', ' Eleven', ' Twelve', ' Thirteen', ' Fourteen', ' Fifteen', ' Sixteen', ' Seventeen', ' Eighteen', ' Nineteen'];
        var tens = ['', 'Ten', ' Twenty', ' Thirty', ' Forty', ' Fifty', ' Sixty', ' Seventy', ' Eighty', ' Ninety'];
    
        numLength = rev.length;
        var word = new Array();
        var j = 0;
    
        for (i = 0; i < numLength; i++) {
            switch (i) {
    
                case 0:
                    if ((rev[i] == 0) || (rev[i + 1] == 1)) {
                        word[j] = '';
                    }
                    else {
                        word[j] = '' + once[rev[i]];
                    }
                    word[j] = word[j];
                    break;
    
                case 1:
                    aboveTens();
                    break;
    
                case 2:
                    if (rev[i] == 0) {
                        word[j] = '';
                    }
                    else if ((rev[i - 1] == 0) || (rev[i - 2] == 0)) {
                        word[j] = once[rev[i]] + " Hundred ";
                    }
                    else {
                        word[j] = once[rev[i]] + " Hundred and";
                    }
                    break;
    
                case 3:
                    if (rev[i] == 0 || rev[i + 1] == 1) {
                        word[j] = '';
                    }
                    else {
                        word[j] = once[rev[i]];
                    }
                    if ((rev[i + 1] != 0) || (rev[i] > 0)) {
                        word[j] = word[j] + " Thousand";
                    }
                    break;
    
                    
                case 4:
                    aboveTens();
                    break;
    
                case 5:
                    if ((rev[i] == 0) || (rev[i + 1] == 1)) {
                        word[j] = '';
                    }
                    else {
                        word[j] = once[rev[i]];
                    }
                    if (rev[i + 1] !== '0' || rev[i] > '0') {
                        word[j] = word[j] + " Lakh";
                    }
                     
                    break;
    
                case 6:
                    aboveTens();
                    break;
    
                case 7:
                    if ((rev[i] == 0) || (rev[i + 1] == 1)) {
                        word[j] = '';
                    }
                    else {
                        word[j] = once[rev[i]];
                    }
                    if (rev[i + 1] !== '0' || rev[i] > '0') {
                        word[j] = word[j] + " Crore";
                    }                
                    break;
    
                case 8:
                    aboveTens();
                    break;
    
                //            This is optional. 
    
                //            case 9:
                //                if ((rev[i] == 0) || (rev[i + 1] == 1)) {
                //                    word[j] = '';
                //                }
                //                else {
                //                    word[j] = once[rev[i]];
                //                }
                //                if (rev[i + 1] !== '0' || rev[i] > '0') {
                //                    word[j] = word[j] + " Arab";
                //                }
                //                break;
    
                //            case 10:
                //                aboveTens();
                //                break;
    
                default: break;
            }
            j++;
        }
    
        function aboveTens() {
            if (rev[i] == 0) { word[j] = ''; }
            else if (rev[i] == 1) { word[j] = twos[rev[i - 1]]; }
            else { word[j] = tens[rev[i]]; }
        }
    
        word.reverse();
        var finalOutput = '';
        for (i = 0; i < numLength; i++) {
            finalOutput = finalOutput + word[i];
        }
        document.getElementById(outputControl).innerHTML = finalOutput;
    }
        </script>
    
    </head>
    
    <body>
    
        <h1>
    
            HTML - Convert numbers to words using JavaScript</h1>
    
        <input id="Text1" type="text" onkeypress="return onlyNumbers(this.value);" onkeyup="NumToWord(this.value,'divDisplayWords');"
    
            maxlength="9" style="background-color: #efefef; border: 2px solid #CCCCC; font-size: large" />
    
        <br />
    
        <br />
    
        <div id="divDisplayWords" style="font-size: 13; color: Teal; font-family: Arial;">
    
        </div>
    
    </body>
    
    </html>

    0 讨论(0)
  • 2020-11-22 15:47

    Update: Looks like this is more useful than I thought. I've just published this on npm. https://www.npmjs.com/package/num-words


    Here's a shorter code. with one RegEx and no loops. converts as you wanted, in south asian numbering system

    var a = ['','one ','two ','three ','four ', 'five ','six ','seven ','eight ','nine ','ten ','eleven ','twelve ','thirteen ','fourteen ','fifteen ','sixteen ','seventeen ','eighteen ','nineteen '];
    var b = ['', '', 'twenty','thirty','forty','fifty', 'sixty','seventy','eighty','ninety'];
    
    function inWords (num) {
        if ((num = num.toString()).length > 9) return 'overflow';
        n = ('000000000' + num).substr(-9).match(/^(\d{2})(\d{2})(\d{2})(\d{1})(\d{2})$/);
        if (!n) return; var str = '';
        str += (n[1] != 0) ? (a[Number(n[1])] || b[n[1][0]] + ' ' + a[n[1][1]]) + 'crore ' : '';
        str += (n[2] != 0) ? (a[Number(n[2])] || b[n[2][0]] + ' ' + a[n[2][1]]) + 'lakh ' : '';
        str += (n[3] != 0) ? (a[Number(n[3])] || b[n[3][0]] + ' ' + a[n[3][1]]) + 'thousand ' : '';
        str += (n[4] != 0) ? (a[Number(n[4])] || b[n[4][0]] + ' ' + a[n[4][1]]) + 'hundred ' : '';
        str += (n[5] != 0) ? ((str != '') ? 'and ' : '') + (a[Number(n[5])] || b[n[5][0]] + ' ' + a[n[5][1]]) + 'only ' : '';
        return str;
    }
    
    document.getElementById('number').onkeyup = function () {
        document.getElementById('words').innerHTML = inWords(document.getElementById('number').value);
    };
    <span id="words"></span>
    <input id="number" type="text" />

    The only limitation is, you can convert maximum of 9 digits, which I think is more than sufficient in most cases..

    0 讨论(0)
提交回复
热议问题