Cómo formatear números como cadenas de moneda

Resuelto Daniel Magliola asked hace 16 años • 66 respuestas

Me gustaría formatear un precio en JavaScript. Me gustaría una función que tome floatcomo argumento y devuelva un stringformato como este:

"$ 2,500.00"

¿Cómo puedo hacer esto?

Daniel Magliola avatar Sep 29 '08 22:09 Daniel Magliola
Aceptado

Formato de número internacional

JavaScript tiene un formateador de números (parte de la API de internacionalización).

// Create our number formatter.
const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',

  // These options are needed to round to whole numbers if that's what you want.
  //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1)
  //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501)
});

console.log(formatter.format(2500)); /* $2,500.00 */
Expandir fragmento

Úselo undefineden lugar del primer argumento ( 'en-US'en el ejemplo) para usar la configuración regional del sistema (la configuración regional del usuario en caso de que el código se ejecute en un navegador). Explicación adicional del código local .

Aquí hay una lista de los códigos de moneda .

Intl.NumberFormat frente a Number.prototype.toLocaleString

Una nota final comparando este con el anterior. toLocaleString. Ambos ofrecen esencialmente la misma funcionalidad. Sin embargo, toLocaleString en sus encarnaciones más antiguas (pre-Intl) en realidad no admite configuraciones regionales : utiliza la configuración regional del sistema. Entonces, al depurar navegadores antiguos, asegúrese de estar usando la versión correcta ( MDN sugiere verificar la existencia deIntl ). No hay necesidad de preocuparse por esto en absoluto si no le importan los navegadores antiguos o simplemente usa el shim .

Además, el rendimiento de ambos es el mismo para un solo elemento, pero si tiene muchos números para formatear, su uso Intl.NumberFormates aproximadamente 70 veces más rápido. Por lo tanto, normalmente es mejor utilizar Intl.NumberFormaty crear una instancia solo una vez por carga de página. De todos modos, aquí está el uso equivalente de toLocaleString:

console.log((2500).toLocaleString('en-US', {
  style: 'currency',
  currency: 'USD',
})); /* $2,500.00 */
Expandir fragmento

Algunas notas sobre la compatibilidad con navegadores y Node.js

  • La compatibilidad con el navegador ya no es un problema hoy en día con más del 99% de compatibilidad a nivel mundial
  • Hay una corrección para admitirlo en navegadores fosilizados (como Internet Explorer 8 ), en caso de que realmente lo necesite.
  • Node.js anterior a v13 solo es compatible en-USde forma inmediata. Una solución es instalar full-icu , consulte aquí para obtener más información.
  • Eche un vistazo a CanIUse para obtener más información.
aross avatar Apr 26 '2013 10:04 aross

Número.prototipo.aFijo

Esta solución es compatible con todos los navegadores principales:

  const profits = 2489.8237;

  profits.toFixed(3) // Returns 2489.824 (rounds up)
  profits.toFixed(2) // Returns 2489.82
  profits.toFixed(7) // Returns 2489.8237000 (pads the decimals)

Todo lo que necesitas es agregar el símbolo de moneda (por ejemplo "$" + profits.toFixed(2)) y tendrás tu monto en dólares.

Función personalizada

Si requiere el uso de ,entre cada dígito, puede utilizar esta función:

function formatMoney(number, decPlaces, decSep, thouSep) {
    decPlaces = isNaN(decPlaces = Math.abs(decPlaces)) ? 2 : decPlaces,
    decSep = typeof decSep === "undefined" ? "." : decSep;
    thouSep = typeof thouSep === "undefined" ? "," : thouSep;
    var sign = number < 0 ? "-" : "";
    var i = String(parseInt(number = Math.abs(Number(number) || 0).toFixed(decPlaces)));
    var j = (j = i.length) > 3 ? j % 3 : 0;

    return sign +
        (j ? i.substr(0, j) + thouSep : "") +
        i.substr(j).replace(/(\decSep{3})(?=\decSep)/g, "$1" + thouSep) +
        (decPlaces ? decSep + Math.abs(number - i).toFixed(decPlaces).slice(2) : "");
}

document.getElementById("b").addEventListener("click", event => {
  document.getElementById("x").innerText = "Result was: " + formatMoney(document.getElementById("d").value);
});
<label>Insert your amount: <input id="d" type="text" placeholder="Cash amount" /></label>
<br />
<button id="b">Get Output</button>
<p id="x">(press button to get output)</p>
Expandir fragmento

Úselo así:

(123456789.12345).formatMoney(2, ".", ",");

Si siempre vas a usar '.' y ',', puede dejarlos fuera de su llamada al método, y el método los predeterminará por usted.

(123456789.12345).formatMoney(2);

Si su cultura tiene los dos símbolos invertidos (es decir, europeos) y le gustaría usar los valores predeterminados, simplemente pegue las siguientes dos líneas en el formatMoneymétodo:

    d = d == undefined ? "," : d,
    t = t == undefined ? "." : t,

Función personalizada (ES6)

Si puedes usar la sintaxis ECMAScript moderna (es decir, a través de Babel), puedes usar esta función más simple en su lugar:

function formatMoney(amount, decimalCount = 2, decimal = ".", thousands = ",") {
  try {
    decimalCount = Math.abs(decimalCount);
    decimalCount = isNaN(decimalCount) ? 2 : decimalCount;

    const negativeSign = amount < 0 ? "-" : "";

    let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(decimalCount)).toString();
    let j = (i.length > 3) ? i.length % 3 : 0;

    return negativeSign +
      (j ? i.substr(0, j) + thousands : '') +
      i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) +
      (decimalCount ? decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "");
  } catch (e) {
    console.log(e)
  }
};

document.getElementById("b").addEventListener("click", event => {
  document.getElementById("x").innerText = "Result was: " + formatMoney(document.getElementById("d").value);
});
<label>Insert your amount: <input id="d" type="text" placeholder="Cash amount" /></label>
<br />
<button id="b">Get Output</button>
<p id="x">(press button to get output)</p>
Expandir fragmento

Solución breve y rápida (¡funciona en todas partes!)

(12345.67).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');  // 12,345.67

La idea detrás de esta solución es reemplazar las secciones coincidentes con la primera coincidencia y coma, es decir '$&,'. La comparación se realiza mediante un enfoque de anticipación . Puede leer la expresión como "hacer coincidir un número si va seguido de una secuencia de tres conjuntos de números (uno o más) y un punto" .

PRUEBAS:

1        --> "1.00"
12       --> "12.00"
123      --> "123.00"
1234     --> "1,234.00"
12345    --> "12,345.00"
123456   --> "123,456.00"
1234567  --> "1,234,567.00"
12345.67 --> "12,345.67"

DEMOSTRACIÓN: http://jsfiddle.net/hAfMM/9571/


Solución corta extendida

También puede ampliar el prototipo de Numberobjeto para agregar soporte adicional para cualquier número de decimales [0 .. n]y el tamaño de grupos de números [0 .. x]:

/**
 * Number.prototype.format(n, x)
 * 
 * @param integer n: length of decimal
 * @param integer x: length of sections
 */
Number.prototype.format = function(n, x) {
    var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\.' : '$') + ')';
    return this.toFixed(Math.max(0, ~~n)).replace(new RegExp(re, 'g'), '$&,');
};

1234..format();           // "1,234"
12345..format(2);         // "12,345.00"
123456.7.format(3, 2);    // "12,34,56.700"
123456.789.format(2, 4);  // "12,3456.79"

DEMOSTRACIÓN / PRUEBAS: http://jsfiddle.net/hAfMM/435/


Solución corta súper extendida

En esta versión súper extendida puedes configurar diferentes tipos de delimitadores:

/**
 * Number.prototype.format(n, x, s, c)
 * 
 * @param integer n: length of decimal
 * @param integer x: length of whole part
 * @param mixed   s: sections delimiter
 * @param mixed   c: decimal delimiter
 */
Number.prototype.format = function(n, x, s, c) {
    var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\D' : '$') + ')',
        num = this.toFixed(Math.max(0, ~~n));

    return (c ? num.replace('.', c) : num).replace(new RegExp(re, 'g'), '$&' + (s || ','));
};

12345678.9.format(2, 3, '.', ',');  // "12.345.678,90"
123456.789.format(4, 4, ' ', ':');  // "12 3456:7890"
12345678.9.format(0, 3, '-');       // "12-345-679"

DEMOSTRACIÓN / PRUEBAS: http://jsfiddle.net/hAfMM/612/

VisioN avatar Jan 20 '2013 19:01 VisioN

Eche un vistazo al objeto Número de JavaScript y vea si puede ayudarle.

  • toLocaleString()formateará un número utilizando un separador de miles específico de la ubicación.
  • toFixed()redondeará el número a un número específico de decimales.

Para usarlos al mismo tiempo, el tipo del valor debe cambiarse nuevamente a un número porque ambos generan una cadena.

Ejemplo:

Number((someNumber).toFixed(1)).toLocaleString()

EDITAR

Uno puede usar toLocaleString directamente y no es necesario volver a convertirlo en un número:

someNumber.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});

Varios números

Si necesita formatear números con frecuencia de manera similar, puede crear un objeto específico para reutilizarlo. Como para alemán (Suiza):

const money = new Intl.NumberFormat('de-CH',
  { style:'currency', currency: 'CHF' });
const percent = new Intl.NumberFormat('de-CH',
  { style:'percent', maximumFractionDigits: 1, signDisplay: "always"});

que se puede utilizar como:

money.format(1234.50); // output CHF 1'234.50
percent.format(0.083);  // output +8.3%

Bastante ingenioso.

17 of 26 avatar Sep 29 '2008 15:09 17 of 26

A continuación se muestra el código de Patrick Desjardins (alias Daok) con algunos comentarios agregados y algunos cambios menores:

/*
decimal_sep: character used as decimal separator, it defaults to '.' when omitted
thousands_sep: char used as thousands separator, it defaults to ',' when omitted
*/
Number.prototype.toMoney = function(decimals, decimal_sep, thousands_sep)
{
   var n = this,
   c = isNaN(decimals) ? 2 : Math.abs(decimals), // If decimal is zero we must take it. It means the user does not want to show any decimal
   d = decimal_sep || '.', // If no decimal separator is passed, we use the dot as default decimal separator (we MUST use a decimal separator)

   /*
   According to [https://stackoverflow.com/questions/411352/how-best-to-determine-if-an-argument-is-not-sent-to-the-javascript-function]
   the fastest way to check for not defined parameter is to use typeof value === 'undefined'
   rather than doing value === undefined.
   */
   t = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, // If you don't want to use a thousands separator you can pass empty string as thousands_sep value

   sign = (n < 0) ? '-' : '',

   // Extracting the absolute value of the integer part of the number and converting to string
   i = parseInt(n = Math.abs(n).toFixed(c)) + '',

   j = ((j = i.length) > 3) ? j % 3 : 0;
   return sign + (j ? i.substr(0, j) + t : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : '');
}

Y aquí algunas pruebas:

// Some tests (do not forget parenthesis when using negative numbers and number with no decimals)
alert(123456789.67392.toMoney() + '\n' + 123456789.67392.toMoney(3) + '\n' + 123456789.67392.toMoney(0) + '\n' + (123456).toMoney() + '\n' + (123456).toMoney(0) + '\n' + 89.67392.toMoney() + '\n' + (89).toMoney());

// Some tests (do not forget parenthesis when using negative numbers and number with no decimals)
alert((-123456789.67392).toMoney() + '\n' + (-123456789.67392).toMoney(-3));

Los cambios menores son:

  1. Movido un poco el Math.abs(decimals)que se debe hacer sólo cuando no es así NaN.

  2. decimal_sepYa no puede ser una cadena vacía (es imprescindible algún tipo de separador decimal )

  3. Usamos typeof thousands_sep === 'undefined'como se sugiere en Cómo determinar mejor si un argumento no se envía a la función JavaScript.

  4. (+n || 0)no es necesario porque thises un Numberobjeto

JSFiddle

Marco Demaio avatar May 19 '2010 14:05 Marco Demaio