Cómo formatear números como cadenas de moneda
Me gustaría formatear un precio en JavaScript. Me gustaría una función que tome float
como argumento y devuelva un string
formato como este:
"$ 2,500.00"
¿Cómo puedo hacer esto?
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 */
Úselo undefined
en 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.NumberFormat
es aproximadamente 70 veces más rápido. Por lo tanto, normalmente es mejor utilizar Intl.NumberFormat
y 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 */
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-US
de 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.
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>
Ú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 formatMoney
mé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>
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 Number
objeto 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/
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.
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:
Movido un poco el
Math.abs(decimals)
que se debe hacer sólo cuando no es asíNaN
.decimal_sep
Ya no puede ser una cadena vacía (es imprescindible algún tipo de separador decimal )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.(+n || 0)
no es necesario porquethis
es unNumber
objeto
JSFiddle