提问者:小点点

用JavaScript将数字转换为文字


我正在做一个代码,将给定的金额转换成文字,这是我在谷歌后得到的。 但我认为要完成一个简单的任务,代码有点长。 两个正则表达式和两个用于循环,我想要更简单的东西。

我正设法使它尽可能短些。 我会把我想出来的贴出来

有什么建议吗?

var th = ['','thousand','million', 'billion','trillion'];
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 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) { // 0235
            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,' ');
}

此外,上面的代码转换成英语的数字系统,如百万/亿,我不想南亚的数字系统。 就像在拉克和克罗


共3个答案

匿名用户

更新:看起来这比我想象的更有用。 我刚在NPM上发表了这个。 https://www.npmjs.com/package/num-words

这里有一个更短的代码。 只有一个正则表达式,没有循环。 按您的要求换算,用南亚编号系统

null

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" />

匿名用户

“看似简单任务。” -Potatoswatter

的确如此。 在这个问题的细节中有许多小恶魔。 解决这个问题很有趣。

编辑:这个更新采取了一个更多的组合方法。 以前有一个很大的函数包装了几个其他的专有函数。 相反,这一次我们定义了可用于多种任务的通用可重用函数。 更多关于那些,在我们看看NumToWords本身…

// 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(' ');
};

以下是依赖关系:

您会注意到,这些几乎不需要文档,因为它们的意图非常清楚。 chunk可能是唯一一个需要花一点时间来消化的,但它确实不是太坏。 另外,函数名给了我们一个很好的指示,它可能是我们以前遇到过的函数。

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))];

“这么说这些让它变得更好了?”

查看代码是如何被显著清理的

// 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)));

最重要的是,我们在新代码中添加的实用工具功能可以在您的App中的其他地方使用。 这意味着,作为以这种方式实现NumToWords的副作用,我们免费获得其他函数。 奖金苏打水!

一些测试

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 

可运行演示

null

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

匿名用户

我花了一段时间来开发一个更好的解决方案。 它可以处理非常大的数字,但一旦它们超过16位,您就把数字作为字符串传入。 关于JavaScript数字的限制。

null

	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 arguemnt 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");