6
\$\begingroup\$

I have created a function which converts numbers into text format and successfully converted up to 6 digit numbers. However, I want to know if I can make any modifications to make the code shorter.

$(document).ready(function() {
  var onesVal = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine'];
  var twoVal = ['Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Ninteen'];
  var tensVal = ["", "", 'Twenty', 'Thirty', 'Fourty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninty'];
  var powersVal = ['Hundred', 'Thousand', 'Lakh'];

  $('input').on('input propertychange', function() {
    if ($(this).val() == '') {
      $('p').text('');
    } else {
      //var textVal = $(this).val();
      var textVal = parseInt($(this).val());
      if (textVal < 10) {
        $('p').text(onesVal[textVal]);
      } else if (textVal < 100) {
        var getTwoVal = forTwoValue(textVal);
        $('p').text(getTwoVal);
      } else if (textVal < 1000) {
        var getThreeVal = forThreeValue(textVal);
        $('p').text(getThreeVal);
      } else if (textVal < 10000) {
        var getFourVal = forFourValue(textVal);
        $('p').text(getFourVal);
      } else if (textVal < 100000) {
        var getFiveVal = forFiveValue(textVal);
        $('p').text(getFiveVal);
      } else if (textVal < 1000000) {
        var getSixVal = forSixValue(textVal);
        $('p').text(getSixVal);
      };
    };
  });

  function forTwoValue(value) {
    value = value.toString();
    if (value < 20) {
      return twoVal[value[1]];
    } else if (value % 10 == 0) {
      return tensVal[value[0]];
    } else {
      return tensVal[value[0]] + ' ' + onesVal[value[1]];
    };
  };

  function forThreeValue(value) {
    value = value.toString();
    if (value % 100 == 0) {
      return onesVal[value[0]] + ' ' + powersVal[0];
    } else {
      var hundOne = Math.floor(value / 100);
      var hundTwo = (value % 100).toString();
      if (hundTwo < 10) {
        return onesVal[hundOne] + ' ' + powersVal[0] + ' ' + onesVal[hundTwo];
      } else if (hundTwo < 100) {
        var getTwoVal = forTwoValue(hundTwo);
        return onesVal[hundOne] + ' ' + powersVal[0] + ' ' + getTwoVal;
      };
    };
  };

  function forFourValue(value) {
    value = value.toString();
    if (value % 1000 == 0) {
      return onesVal[value[0]] + ' ' + powersVal[1];
    } else {
      var thouOne = Math.floor(value / 1000);
      var thouTwo = (value % 1000).toString();
      if (thouTwo < 10) {
        return onesVal[thouOne] + ' ' + powersVal[1] + ' ' + onesVal[thouTwo];
      } else if (thouTwo < 100) {
        var getTwoVal = forTwoValue(thouTwo);
        return onesVal[thouOne] + ' ' + powersVal[0] + ' ' + getTwoVal;
      } else if (thouTwo < 1000) {
        var getThreeVal = forThreeValue(thouTwo);
        return onesVal[thouOne] + ' ' + powersVal[1] + ' ' + getThreeVal;
      };
    };
  };

  function forFiveValue(value) {
    value = value.toString();
    var tenThouOne = Math.floor(value / 1000).toString();
    var tenThouTwo = (value % 1000).toString();
    var getTwoVal = forTwoValue(tenThouOne);
    if (tenThouTwo == 0) {
      return getTwoVal + ' ' + powersVal[1];
    } else if (tenThouTwo < 10) {
      return getTwoVal + ' ' + powersVal[1] + ' ' + onesVal[tenThouTwo];
    } else if (tenThouTwo < 100) {
      var getTwoVal2 = forTwoValue(tenThouTwo);
      return getTwoVal + ' ' + powersVal[1] + ' ' + getTwoVal2;
    } else if (tenThouTwo < 1000) {
      var getThreeVal = forThreeValue(tenThouTwo);
      return getTwoVal + ' ' + powersVal[1] + ' ' + getThreeVal;
    };
  };

  function forSixValue(value) {
    value = value.toString();
    if (value % 100000 == 0) {
      return onesVal[value[0]] + ' ' + powersVal[2];
    } else {
      var lakhOne = Math.floor(value / 100000).toString();
      var lakhTwo = (value % 100000).toString();
      if (lakhTwo == 0) {
        return onesVal[lakhOne] + ' ' + powersVal[2];
      } else if (lakhTwo < 10) {
        return onesVal[lakhOne] + ' ' + powersVal[2] + ' ' + onesVal[lakhTwo];
      } else if (lakhTwo < 100) {
        var getTwoVal = forTwoValue(lakhTwo);
        return onesVal[lakhOne] + ' ' + powersVal[2] + ' ' + getTwoVal;
      } else if (lakhTwo < 1000) {
        var getThreeVal = forThreeValue(lakhTwo);
        return onesVal[lakhOne] + ' ' + powersVal[2] + ' ' + getThreeVal;
      } else if (lakhTwo < 10000) {
        var getFourVal = forFourValue(lakhTwo);
        return onesVal[lakhOne] + ' ' + powersVal[2] + ' ' + getFourVal;
      } else if (lakhTwo < 100000) {
        var getFiveVal = forFiveValue(lakhTwo);
        return onesVal[lakhOne] + ' ' + powersVal[2] + ' ' + getFiveVal;
      };
    };
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Enter Number : <input type="text" maxlength="6">

<p id="demo"></p>

\$\endgroup\$

4 Answers 4

4
\$\begingroup\$

A few thoughts:

  • The whole logic seems much too hard-coded. Think about the problem, which is that you basically need to take the string representation of the number and grab a group of the last three characters off the string at a time to process, the whole time tracking the number of "groups" that have been processed to be able to add "thousand", "million", etc. to it. Think about breaking apart your function along those lines instead of having a bunch of for*Value functions.
  • You should only have one point in our code where you are writing to the target p element after the word string has been built. There is no reason to repeat ('p').text(*); throughout your code.
  • Consider encapsulating all your conversion logic under one function such that your code in the event handler can be greatly simplified and you could re-use this function elsewhere.

For example:

$('input').on('input propertychange', function() {
  $('p').text(
    // numberToWords() could be included from another file
    numberToWords($(this).val())
  );
}
  • In general, I would recommend you getting in the habit of using exact comparisons (=== and !==) instead of loose comparisons as default behavior. This will make your code less fragile to unexpected truthy/falsey behavior. Use loose comparisons only where you have a specific use case to do so.
  • I don't think "zero" needs to be in your array, as this is an edge case that should be handled upfront before the main logic.
\$\endgroup\$
1
\$\begingroup\$
  • You can combine the first twenty numbers. Instead of doing:

    if (textVal < 10)
      onesVal[textVal]
    ...
    if (textVal > 10 && textVal < 20) 
      twosVal[textVal]
    

    Just create a single array and write

    if (textVal < 20)
      oneOrTwosVal[textVal]
    
    • Group your code into groups of three digits and reuse that so given

      function forThreeValue ...

    You can write

    if (value >= 1000)
      return forThreeValue(value / 1000) + ' thousand and ' + forThreeValue(value % 1000)
    else
      return forThreeValue(value);
    

    (You will have some corner cases for example 1000)

\$\endgroup\$
1
\$\begingroup\$

Numbers to Words

To me this is a recursive problem. We start by breaking the problem down into numbers in three digit sets.

1a. Terms: We need some way to lookup values, numerically, arrays are perfect for this.

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

1b. Larger numbers: any number above 99 has unique names, "hundred", "thousand", "million" etc.

const   ILLIONS = [3,4,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60,63,100,101,303];
    ILLIONS.words = {
        3: "hundred",
        4: "thousand",
        6: "million",
        9: "milliard",
        9: "billion",
        12: "trillion",
        15: "quadrillion",
        18: "quintillion",
        21: "sextillion",
        24: "septillion",
        27: "octillion",
        30: "nonillion",
        33: "decillion",
        36: "undecillion",
        39: "duodecillion",
        42: "tredecillion",
        45: "quattuordecillion",
        48: "quindecillion",
        51: "sexdecillion",
        54: "septendecillion",
        57: "octodecillion",
        60: "novemdecillion",
        63: "vigintillion",
        10: "google",
        10: "googlePlex",
        30: "centillion"
    }
  1. Parsing large numbers, 100 or more.

    Our objective here is to break the number down into 3 digit units.

    We will use our ILLIONS array to lookup our words for the bigger numbers

    We simply go through each of these, ILLIONS[n], largest to smallest.

  2. Parsing numbers less than 100;

    // Pseudo Code

    if( number > 20 ){

    n = ( number / 10 ), ignoring the remainder
    
    using n, we should be able to find a matching value in the "TENTHS" array;
    
    NOTE: we are exploiting arrays here, so that the product of our calculation
    (number / 10) will be an index to a value which is the word version of the 
    number we are looking for.
    
    add that word TENTHS[n] to the words array.
    
    assign ( number % 10 ) to number variable
    

    }

    if( number > 0 ){

    Essentially the same trick here, except we will 
    lookup number in the LESS_TWENTY array, pushing 
    it's value into the words array
    

    }

    if( decimals ){

    Now we need to address decimals, this is simply a 
    recursive call back to our numbersToWords function, 
    only we tack on "point" and then the results of numbersToWords
    to the words array.
    

    }

That's the basic idea, here is some code, with some additional checks, that does the job nicely.

const 	SUBTWENTY = [ null, 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'];
const 	DECA = [ null, 'ten', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'];
const 	ILLIONS = [3,4,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60,63,100,101,303];
	ILLIONS.words = {3: "hundred",4: "thousand", 6: "million", 9: "milliard", 9: "billion", 12: "trillion", 15: "quadrillion", 18: "quintillion", 21: "sextillion", 24: "septillion", 27: "octillion", 30: "nonillion", 33: "decillion", 36: "undecillion", 39: "duodecillion", 42: "tredecillion", 45: "quattuordecillion", 48: "quindecillion", 51: "sexdecillion", 54: "septendecillion", 57: "octodecillion", 60: "novemdecillion", 63: "vigintillion", 10: "google",	10: "googlePlex", 30: "centillion"}
		

function numbersToWords(num, words, decimal, segments){
  
  words = [], decimal = ((Math.abs(num) % 1) * 100).toFixed() || 0;
  if(num < 0) words.push('negative');
  num = Math.floor(Math.abs(num)).toFixed();
  
  segments = [];
  
  for(var i=ILLIONS.length; i > 0; i--){
    if( ILLIONS[i] <= num.length ) segments.push(ILLIONS[i]);
  }
  
  while(num){ 
    let sub = num.slice(0, Math.max(0, num.length % 3 || 3));
    if(Number(sub) > 0 ) words.push(hundredsToWords(sub), ILLIONS.words[segments.shift()]);
    num = num.slice(num.length % 3 || 3);
  }
  
  if( !!decimal ){
    words.push('point', hundredsToWords(Number(decimal)));
  }

  return words.join(' ').replace('  ', ' ');
   
}

function hundredsToWords(num, words){

  num = String(Math.floor(num).toFixed());
    
  words = [];
  
  if(num.length > 2 ){
    if( num[1] === '0' ){
      words.push(SUBTWENTY[num[0]], ILLIONS.words[num.length]);      
      num = num.slice(2);
    } else {
      words.push(hundredsToWords(num.slice(0,1)), ILLIONS.words[num.length ]);
      num = num.slice(1);
    } 
    if(num[0] !== '0') words.push('and');      
  }
  
  if(num > 20){
    words.push(DECA[num[0]]);
    num = num[1];
  }
  
  if(num > 0 && num < 20) words.push(SUBTWENTY[Number(num)]);
  
  return words.join(' ');
}


console.log(numbersToWords(10234.44));
console.log(numbersToWords(-10234.44));

\$\endgroup\$
3
  • \$\begingroup\$ I like your answer but I think it has a bug, try 100234. codepen.io/imjosh/pen/WjNOqa?editors=1011 \$\endgroup\$
    – imjosh
    Apr 12, 2017 at 19:08
  • \$\begingroup\$ yeah, i would need to track down the error, i believe it's a combination of rounding errors, and issues with lose of precision somewhere. \$\endgroup\$
    – r10y
    Apr 12, 2017 at 22:40
  • \$\begingroup\$ fixed it, I reworked a few things, now it will work for numbers upto a centillion \$\endgroup\$
    – r10y
    Apr 13, 2017 at 9:48
0
\$\begingroup\$

All you really need are two things: first, a function to get you up to a three-digit value. Then, you just go through each thousand, find the 3-digit value for those 3 numbers (i.e. the 1,000's value for 340,000 is Three-Hundred and Forty). Here's the code for it:

$(document).ready(function() {
  var onesVal = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Ninteen'];
  var tensVal = ["", "", 'Twenty', 'Thirty', 'Fourty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninty'];
  var thousandsPowers = ['Thousand', 'Million', 'Billion', 'Trillion', 'Quadrilion'];

  $('input').on('input propertychange', function() {
    if ($(this).val() == '') {
      $('p').text('');
    } else {
      var textVal = parseInt($(this).val()); 
      if (textVal == 0) $('p').text('Zero');
       else {
       var greaterVal = forGreaterValue(textVal); 
        $('p').text(greaterVal);
      }
    };
  });

  function forLowerVal(value) {
    value = value.toString();
    if (value < 20) {
      return onesVal[value];
    } else {
      return tensVal[Math.floor(value/10)] + (value%10 != 0 ? '-' + onesVal[value % 10] : '');
    }
  }
  function forThreeValue(value) {
    value = value.toString();
    if (value < 100) return forLowerVal(value);
    else return onesVal[Math.floor(value/100)] + ' Hundred' + (value % 100 != 0 ? ' and ' + forLowerVal(value % 100) : '');
  };
  
  function forGreaterValue(value) {
    var currentPower = 0;
    var outputString = "";
    outputString += forThreeValue(value%1000);
    value = Math.floor(value/1000);
    while (value > 0) {
      console.log(value);
      outputString = (value%1000 != 0 ? forThreeValue(value%1000) + ' ' + thousandsPowers[currentPower] + ' ' : '') + outputString;
      value = Math.floor(value/1000);
      currentPower += 1;
    }
    return outputString;
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Enter Number : <input type="text">

<p id="demo"></p>

Just add more after Quadrillion, and it should scale infinitely.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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