How can I format numbers as currency string in JavaScript?

How can I format numbers as currency string in JavaScript?

I would like to format a price in JavaScript.
I’d like a function which takes a float as an argument and returns a string formatted like this:
“$ 2,500.00”

What’s the best way to do this?

Solutions/Answers:

Solution 1:

Number.prototype.toFixed

This solution is compatible with every single major browser:

  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)

All you need is to add the currency symbol (e.g. "$" + profits.toFixed(2)) and you will have your amount in dollars.

Custom function

If you require the use of , between each digit, you can use this function:

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>

Use it like so:

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

If you’re always going to use ‘.’ and ‘,’, you can leave them off your method call, and the method will default them for you.

(123456789.12345).formatMoney(2);

If your culture has the two symbols flipped (i.e. Europeans) and you would like to use the defaults, just paste over the following two lines in the formatMoney method:

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

Custom function (ES6)

If you can use modern ECMAScript syntax (i.e. through Babel), you can use this simpler function instead:

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>

Solution 2:

Short and fast solution (works everywhere!)

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

The idea behind this solution is replacing matched sections with first match and comma, i.e. '$&,'. The matching is done using lookahead approach. You may read the expression as “match a number if it is followed by a sequence of three number sets (one or more) and a dot”.

TESTS:

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"

DEMO: http://jsfiddle.net/hAfMM/9571/


Extended short solution

You can also extend the prototype of Number object to add additional support of any number of decimals [0 .. n] and the size of number groups [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"

DEMO / TESTS: http://jsfiddle.net/hAfMM/435/


Super extended short solution

In this super extended version you may set different delimiter types:

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

DEMO / TESTS: http://jsfiddle.net/hAfMM/612/

Solution 3:

Intl.numberformat

Javascript has a number formatter (part of the Internationalization API).

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

formatter.format(2500); /* $2,500.00 */

JS fiddle

Use undefined in place of the first argument ('en-US' in the example) to use the system locale (the user locale in case the code is running in a browser).

Intl.NumberFormat vs Number.prototype.toLocaleString

A final note comparing this to the older .toLocaleString. They both offer essentially the same functionality. However, toLocaleString in its older incarnations (pre-Intl) does not actually support locales: it uses the system locale. Therefore, be sure that you’re using the correct version (MDN suggests to check for the existence of Intl). Also, the performance of both is the same for a single item, but if you have a lot of numbers to format, using Intl.NumberFormat is ~70 times faster. Here’s how to use toLocaleString:

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

Some notes on browser support

  • Browser support is no longer an issue nowadays with 97% support in the US/EU
  • For other parts of the world (92% supported), the biggest offenders in terms of support are UC Mobile (stay away from that) and Opera Mini (crippled by design)
  • There is a shim to support it on older browsers
  • Have a look at CanIUse for more info

Solution 4:

Take a look at the JavaScript Number object and see if it can help you.

  • toLocaleString() will format a number using location specific thousands separator.
  • toFixed() will round the number to a specific number of decimal places.

To use these at the same time the value must have its type changed back to a number because they both output a string.

Example:

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

Solution 5:

Below is the Patrick Desjardins (alias Daok) code with a bit of comments added and some minor changes:

/* 
decimal_sep: character used as deciaml separtor, 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 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) : ''); 
}

and here some tests:

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

The minor changes are:

  1. moved a bit the Math.abs(decimals) to be done only when is not NaN.

  2. decimal_sep can not be empty string anymore (a some sort of decimal separator is a MUST)

  3. we use typeof thousands_sep === 'undefined' as suggested in How best to determine if an argument is not sent to the JavaScript function

  4. (+n || 0) is not needed because this is a Number object

JS Fiddle

Solution 6:

accounting.js is a tiny JavaScript library for number, money and currency formatting.