I\'m trying to convert numbers into english words, for example 1234 would become: \"one thousand two hundred thirty four\".
My Tact
Here's a solution that will handle any integer value that fit's in a string. I've defined number scales up to "decillion", so this solution should be accurate up to 999 decillion. After which you get things like "one thousand decillion" and so on.
JavaScript numbers start to fail around "999999999999999" so the convert function works with strings of numbers only.
Examples:
convert("365");
//=> "three hundred sixty-five"
convert("10000000000000000000000000000230001010109");
//=> "ten thousand decillion two hundred thirty billion one million ten thousand one hundred nine"
Code:
var lt20 = ["", "one", "two", "three", "four", "five", "six", "seven","eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" ],
tens = ["", "ten", "twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eightty", "ninety" ],
scales = ["", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sextillion", "septillion", "octillion", "nonillion", "decillion" ],
max = scales.length * 3;
function convert(val) {
var len;
// special cases
if (val[0] === "-") { return "negative " + convert(val.slice(1)); }
if (val === "0") { return "zero"; }
val = trim_zeros(val);
len = val.length;
// general cases
if (len < max) { return convert_lt_max(val); }
if (len >= max) { return convert_max(val); }
}
function convert_max(val) {
return split_rl(val, max)
.map(function (val, i, arr) {
if (i < arr.length - 1) {
return convert_lt_max(val) + " " + scales.slice(-1);
}
return convert_lt_max(val);
})
.join(" ");
}
function convert_lt_max(val) {
var l = val.length;
if (l < 4) {
return convert_lt1000(val).trim();
} else {
return split_rl(val, 3)
.map(convert_lt1000)
.reverse()
.map(with_scale)
.reverse()
.join(" ")
.trim();
}
}
function convert_lt1000(val) {
var rem, l;
val = trim_zeros(val);
l = val.length;
if (l === 0) { return ""; }
if (l < 3) { return convert_lt100(val); }
if (l === 3) { //less than 1000
rem = val.slice(1);
if (rem) {
return lt20[val[0]] + " hundred " + convert_lt1000(rem);
} else {
return lt20[val[0]] + " hundred";
}
}
}
function convert_lt100(val) {
if (is_lt20(val)) { // less than 20
return lt20[val];
} else if (val[1] === "0") {
return tens[val[0]];
} else {
return tens[val[0]] + "-" + lt20[val[1]];
}
}
function split_rl(str, n) {
// takes a string 'str' and an integer 'n'. Splits 'str' into
// groups of 'n' chars and returns the result as an array. Works
// from right to left.
if (str) {
return Array.prototype.concat
.apply(split_rl(str.slice(0, (-n)), n), [str.slice(-n)]);
} else {
return [];
}
}
function with_scale(str, i) {
var scale;
if (str && i > (-1)) {
scale = scales[i];
if (scale !== undefined) {
return str.trim() + " " + scale;
} else {
return convert(str.trim());
}
} else {
return "";
}
}
function trim_zeros(val) {
return val.replace(/^0*/, "");
}
function is_lt20(val) {
return parseInt(val, 10) < 20;
}
A little code snippet for INDONESIAN DEVELOPER to translate numeric value into verbal spelling (minimized by hand, only 576 bytes, requires ES6+)...
const terbilang=(x,nol='---',min='minus')=>{const S=['','satu','dua','tiga','empat','lima','enam','tujuh','delapan','sembilan'],K=['','ribu','juta','miliar','triliun','kuadriliun'],s=[];if(!x)return nol;if(x<0){if(min)s=[min];x=-x}for(let k=0;x;k++){let g=x%1e3;if(g===1&&k===1)s.unshift('seribu');else{let u=g>99?[(g>199?S[g/100|0]+' ':'se')+'ratus']:[];if(g%=100){if(g>9&&g<20)u.push(g<11?'sepuluh':(g<12?'se':S[g%10]+' ')+'belas');else{if(g>19)u.push(S[g/10|0]+' puluh');if(g%=10)u.push(S[g])}}k&&u.push(K[k]);s.unshift(u.join(' '))}x=Math.floor(x/1e3)}return s.join(' ')};
Indian Version
Updated version of @jasonhao 's answer for Indian currency
function intToEnglish(number){
var NS = [
{value: 10000000, str: "Cror"},
{value: 100000, str: "Lakhs"},
{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<=90){
result += n.str;
number -= n.value;
if(number>0) result += ' ';
}else{
var t = Math.floor(number / n.value);
console.log(t);
var d = number % n.value;
if(d>0){
return intToEnglish(t) + ' ' + n.str +' ' + intToEnglish(d);
}else{
return intToEnglish(t) + ' ' + n.str;
}
}
}
}
return result;
}
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;
}
}
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<script type="text/javascript">
var th = ['', ' thousand', ' million', ' billion', ' trillion', ' quadrillion', ' quintillion'];
var dg = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
var tn = ['ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'];
var tw = ['twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'];
function update(){
var numString = document.getElementById('number').value;
if (numString == '0') {
document.getElementById('container').innerHTML = 'Zero';
return;
}
if (numString == 0) {
document.getElementById('container').innerHTML = 'messeg tell to enter numbers';
return;
}
var output = toWords(numString);
//print the output
document.getElementById('container').innerHTML = output;
}
function toWords(s) {
s = s.toString();
s = s.replace(/[\, ]/g, '');
if (s != parseFloat(s)) return 'not a number';
var x = s.indexOf('.');
if (x == -1) x = s.length;
if (x > 15) return 'too big';
var n = s.split('');
var str = '';
var sk = 0;
for (var i = 0; i < x; i++) {
if ((x - i) % 3 == 2) {
if (n[i] == '1') {
str += tn[Number(n[i + 1])] + ' ';
i++;
sk = 1;
} else if (n[i] != 0) {
str += tw[n[i] - 2] + ' ';
sk = 1;
}
} else if (n[i] != 0) {
str += dg[n[i]] + ' ';
if ((x - i) % 3 == 0) str += 'hundred ';
sk = 1;
}
if ((x - i) % 3 == 1) {
if (sk) str += th[(x - i - 1) / 3] + ' ';
sk = 0;
}
}
if (x != s.length) {
var y = s.length;
str += 'point ';
for (var i = x + 1; i < y; i++) str += dg[n[i]] + ' ';
}
return str.replace(/\s+/g, ' ');
}
</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>
I would like to point out that the original logic fails for values between x11-x19, where x >= 1. For example, 118 returns "one hundred eight". This is because these numbers are processed by the following code in triConvert():
//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;
}
here, the character representing the tens digit is used to index the tens[]
array, which has an empty string at index [1], so 118 become 108 in effect.
It might be better to deal with the hundreds (if any first), then run the ones and tens through the same logic. Instead of:
//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;
I would suggest:
// 100 and more
if ( numString.length == 3 )
{
output = hundreds[ parseInt( numString.charAt(0) ) ] + hundred ;
num = num % 100 ;
numString = num.toString() ;
}
if ( num < 20 )
{
output += ones[num] ;
}
else
{ // 20-99
output += tens[ parseInt( numString.charAt(0) ) ] ;
output += '-' + ones[ parseInt( numString.charAt(1) ) ] ;
}
return output;
It seems to me that the suggested code is both shorter and clearer, but I might be biased ;-)