Transforma números en palabras en el sistema lakh / crore

Resuelto Jeo asked hace 11 años • 35 respuestas

Estoy escribiendo un código que convierte un número determinado en palabras, esto es lo que obtuve después de buscar en Google. Pero creo que es demasiado largo para una tarea tan sencilla. Dos expresiones regulares y dos forbucles, quiero algo más sencillo.

Estoy tratando de lograr esto en la menor cantidad de líneas de código posible. esto es lo que se me ocurrió hasta ahora:

¿Alguna sugerencia?

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

Además, el código anterior se convierte al sistema de numeración inglés como Million/Billion, necesito el sistema de numeración del sur de Asia, como en Lakhs y Crores.

Jeo avatar Feb 08 '13 13:02 Jeo
Aceptado

Actualización : Parece que esto es más útil de lo que pensaba. Acabo de publicar esto en npm. https://www.npmjs.com/package/num-words


Aquí hay un código más corto. con una expresión regular y sin bucles. convierte como quisieras, en el sistema de numeración del sur de Asia

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

La única limitación es que puedes convertir un máximo de 9 dígitos, lo que creo que es más que suficiente en la mayoría de los casos.

Salman avatar Feb 08 '2013 06:02 Salman

" Tarea engañosamente simple." – matapatatas

En efecto. Hay muchos pequeños demonios merodeando por los detalles de este problema. Aunque fue muy divertido resolverlo.

EDITAR: Esta actualización adopta un enfoque mucho más compositivo. Anteriormente había una función grande que envolvía un par de funciones propietarias más. En cambio, esta vez definimos funciones genéricas reutilizables que podrían usarse para muchas variedades de tareas. Más sobre ellos después de que nos echemos un vistazo a numToWordssí mismos...

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

Aquí están las dependencias:

Notarás que estos casi no requieren documentación porque sus intenciones son inmediatamente claras. chunkPuede que sea el único que tarda un momento en digerirse, pero en realidad no es tan malo. Además, el nombre de la función nos da una indicación bastante buena de lo que hace, y probablemente sea una función que hayamos encontrado antes.

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

"¿Entonces esto lo hace mejor?"

Mira cómo el código se ha limpiado significativamente.

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

Lo más importante es que las funciones de utilidad que agregamos en el nuevo código se pueden usar en otros lugares de su aplicación. Esto significa que, como efecto secundario de implementar numToWordsde esta manera, obtenemos las otras funciones de forma gratuita. ¡Refresco extra!

Algunas pruebas

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 

Demostración ejecutable

Mostrar fragmento de código


Puedes transpilar el código usando babel.js si quieres ver la variante ES5

Mulan avatar May 29 '2015 08:05 Mulan