views:

7711

answers:

11

I would like to format a price in JavaScript.
Basically, I have a float variable, and I'd like to have a function that will receive that variable, and output:

"$ 2,500.00"

What's the best way to do this?

EDIT: OK, since I haven't gotten any answers better than the code I implemented, plus my own answer has been voted down and I can't put my own answer as the right one... Here it is...

var DecimalSeparator = Number("1.2").toLocaleString().substr(1,1);

var AmountWithCommas = Amount.toLocaleString();
var arParts = String(AmountWithCommas).split(DecimalSeparator);
var intPart = arParts[0];
var decPart = (arParts.length > 1 ? arParts[1] : '');
decPart = (decPart + '00').substr(0,2);

return '£ ' + intPart + DecimalSeparator + decPart;
A: 

The main part is inserting the thousand-separators, that could be done like this:

<script type="text/javascript">
function ins1000Sep(val){
  val = val.split(".");
  val[0] = val[0].split("").reverse().join("");
  val[0] = val[0].replace(/(\d{3})/g,"$1,");
  val[0] = val[0].split("").reverse().join("");
  val[0] = val[0].indexOf(",")==0?val[0].substring(1):val[0];
  return val.join(".");
}
function rem1000Sep(val){
  return val.replace(/,/g,"");
}
function formatNum(val){
  val = Math.round(val*100)/100;
  val = (""+val).indexOf(".")>-1 ? val + "00" : val + ".00";
  var dec = val.indexOf(".");
  return dec == val.length-3 || dec == 0 ? val : val.substring(0,dec+3);
}
</script>

<button onclick="alert(ins1000Sep(formatNum(12313231)));">
roenving
+20  A: 

You can use :

  var profits=2489.8237
  profits.toFixed(3) //returns 2489.824 (round up)
  profits.toFixed(2) //returns 2489.82
  profits.toFixed(7) //returns 2489.8237000 (padding)

Then you can add the sign of '$'.

If you require ',' for thousand you can use:

Number.prototype.formatMoney = function(c, d, t){
var n = this, c = isNaN(c = Math.abs(c)) ? 2 : c, d = d == undefined ? "," : d, t = t == undefined ? "." : t, s = n < 0 ? "-" : "", i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
   return s + (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 use it with :

(123456789.12345).formatMoney(2, '.', ',');
Daok
@Daok: formatMoney is poetry, thanks and +1. BTW: if one day you have some spare time I wrote again your code as an answer below. I added some comments and minor consideartion, plz have a look and let me know your thoughts.
Marco Demajo
This answer is quite old, but I must add a comment and say that the `formatMoney` function is mad genius -- that's some nice, concise, insane code.
Nathan Loding
If you always want to round up on 5 and down below 5, you cannot rely on toFixed() because of standard issues involved in representing floating point in binary. For example, try `(1.005).toFixed(2)`.
Ryan
+9  A: 

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.

Unfortunately, you can't use them at the same time :P.

17 of 26
Thanks! Based on this idea I was able to make one that is short and simple enough! (and localized)Excellent.
Daniel Magliola
+1  A: 
function CurrencyFormatted(amount)
{
    var i = parseFloat(amount);
    if(isNaN(i)) { i = 0.00; }
    var minus = '';
    if(i < 0) { minus = '-'; }
    i = Math.abs(i);
    i = parseInt((i + .005) * 100);
    i = i / 100;
    s = new String(i);
    if(s.indexOf('.') < 0) { s += '.00'; }
    if(s.indexOf('.') == (s.length - 2)) { s += '0'; }
    s = minus + s;
    return s;
}

From WillMaster.

Bill the Lizard
A: 

The YUI codebase uses the following formmating:

format: function(nData, oConfig) { oConfig = oConfig || {};

if(!YAHOO.lang.isNumber(nData)) {
    nData *= 1;
}

if(YAHOO.lang.isNumber(nData)) {
    var sOutput = nData + "";
    var sDecimalSeparator = (oConfig.decimalSeparator) ? oConfig.decimalSeparator : ".";
    var nDotIndex;

    // Manage decimals
    if(YAHOO.lang.isNumber(oConfig.decimalPlaces)) {
        // Round to the correct decimal place
        var nDecimalPlaces = oConfig.decimalPlaces;
        var nDecimal = Math.pow(10, nDecimalPlaces);
        sOutput = Math.round(nData*nDecimal)/nDecimal + "";
        nDotIndex = sOutput.lastIndexOf(".");

        if(nDecimalPlaces > 0) {
            // Add the decimal separator
            if(nDotIndex < 0) {
                sOutput += sDecimalSeparator;
                nDotIndex = sOutput.length-1;
            }
            // Replace the "."
            else if(sDecimalSeparator !== "."){
                sOutput = sOutput.replace(".",sDecimalSeparator);
            }
            // Add missing zeros
            while((sOutput.length - 1 - nDotIndex) < nDecimalPlaces) {
                sOutput += "0";
            }
        }
    }

    // Add the thousands separator
    if(oConfig.thousandsSeparator) {
        var sThousandsSeparator = oConfig.thousandsSeparator;
        nDotIndex = sOutput.lastIndexOf(sDecimalSeparator);
        nDotIndex = (nDotIndex > -1) ? nDotIndex : sOutput.length;
        var sNewOutput = sOutput.substring(nDotIndex);
        var nCount = -1;
        for (var i=nDotIndex; i>0; i--) {
            nCount++;
            if ((nCount%3 === 0) && (i !== nDotIndex)) {
                sNewOutput = sThousandsSeparator + sNewOutput;
            }
            sNewOutput = sOutput.charAt(i-1) + sNewOutput;
        }
        sOutput = sNewOutput;
    }

    // Prepend prefix
    sOutput = (oConfig.prefix) ? oConfig.prefix + sOutput : sOutput;

    // Append suffix
    sOutput = (oConfig.suffix) ? sOutput + oConfig.suffix : sOutput;

    return sOutput;
}
// Still not a Number, just return unaltered
else {
    return nData;
}

}

it would need editing as the YUI library is configurable, like replacing oConfig.decimalSeparator with "."

AlbertEin
Too long, and i'd have to include YUI
Daniel Magliola
+2  A: 

Ok, based on what you said, i'm using this:

var DecimalSeparator = Number("1.2").toLocaleString().substr(1,1);

var AmountWithCommas = Amount.toLocaleString();
var arParts = String(AmountWithCommas).split(DecimalSeparator);
var intPart = arParts[0];
var decPart = (arParts.length > 1 ? arParts[1] : '');
decPart = (decPart + '00').substr(0,2);

return '£ ' + intPart + DecimalSeparator + decPart;

I'm open to improvement suggestions (i'd prefer not to include YUI just to do this :-) ) I already know I should be detecting the "." instead of just using it as the decimal separator...

Daniel Magliola
Note that your version doesn't properly round to two decimal digits. For example, 3.706 would be formatted as "£ 3.70", not as "£ 3.71" as it's supposed to be.
Ates Goral
Yes, that's OK in my particular case, since the amounts I'm working with already have at most 2 digitsThe reason I need to fix to 2 decimals is for amounts with no decimals or with only 1.
Daniel Magliola
This worked really well.
davethegr8
A: 

Minimalistic approach that just meets the original requirements:

function formatMoney(n) {
    return "$ " + (Math.round(n * 100) / 100).toLocaleString();
}

@Daniel Magliola: You're right, the above was a hasty, incomplete implementation. Here's the corrected implementation:

function formatMoney(n) {
    return "$ " + n.toLocaleString().split(".")[0] + "."
        + n.toFixed(2).split(".")[1];
}
Ates Goral
Sorry, no. That will remove extra decimal places, but it won't fix to 2 decimal positions. "25" will be "$ 25" with your code, not "$ 25.00"
Daniel Magliola
Fixed implementation above.
Ates Goral
Still wrong!You're using toLocaleString, which can make the decimal separator "," instead of ".", and assuming it's "."
Daniel Magliola
This was a "minimalistic" approach to meet the original vague requirements that just gave "$ 2,500.00" as an example.
Ates Goral
A: 

There is a javascript port of the PHP function "number_format".

I find it very usefull as it is easy to use and recognisable for PHP developers.

function number_format (number, decimals, dec_point, thousands_sep) {
// Formats a number with grouped thousands
//
// version: 906.1806
// discuss at: http://phpjs.org/functions/number_format
// +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// +     bugfix by: Michael White (http://getsprink.com)
// +     bugfix by: Benjamin Lupton
// +     bugfix by: Allan Jensen (http://www.winternet.no)
// +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
// +     bugfix by: Howard Yeend
// +    revised by: Luke Smith (http://lucassmith.name)
// +     bugfix by: Diogo Resende
// +     bugfix by: Rival
// +     input by: Kheang Hok Chin (http://www.distantia.ca/)
// +     improved by: davook
// +     improved by: Brett Zamir (http://brett-zamir.me)
// +     input by: Jay Klehr
// +     improved by: Brett Zamir (http://brett-zamir.me)
// +     input by: Amir Habibi (http://www.residence-mixte.com/)
// +     bugfix by: Brett Zamir (http://brett-zamir.me)
// *     example 1: number_format(1234.56);
// *     returns 1: '1,235'
// *     example 2: number_format(1234.56, 2, ',', ' ');
// *     returns 2: '1 234,56'
// *     example 3: number_format(1234.5678, 2, '.', '');
// *     returns 3: '1234.57'
// *     example 4: number_format(67, 2, ',', '.');
// *     returns 4: '67,00'
// *     example 5: number_format(1000);
// *     returns 5: '1,000'
// *     example 6: number_format(67.311, 2);
// *     returns 6: '67.31'
// *     example 7: number_format(1000.55, 1);
// *     returns 7: '1,000.6'
// *     example 8: number_format(67000, 5, ',', '.');
// *     returns 8: '67.000,00000'
// *     example 9: number_format(0.9, 0);
// *     returns 9: '1'
// *     example 10: number_format('1.20', 2);
// *     returns 10: '1.20'
// *     example 11: number_format('1.20', 4);
// *     returns 11: '1.2000'
// *     example 12: number_format('1.2000', 3);
// *     returns 12: '1.200'
var n = number, prec = decimals;

var toFixedFix = function (n,prec) {
    var k = Math.pow(10,prec);
    return (Math.round(n*k)/k).toString();
};

n = !isFinite(+n) ? 0 : +n;
prec = !isFinite(+prec) ? 0 : Math.abs(prec);
var sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep;
var dec = (typeof dec_point === 'undefined') ? '.' : dec_point;

var s = (prec > 0) ? toFixedFix(n, prec) : toFixedFix(Math.round(n), prec); //fix for IE parseFloat(0.55).toFixed(0) = 0;

var abs = toFixedFix(Math.abs(n), prec);
var _, i;

if (abs >= 1000) {
    _ = abs.split(/\D/);
    i = _[0].length % 3 || 3;

    _[0] = s.slice(0,i + (n < 0)) +
          _[0].slice(i).replace(/(\d{3})/g, sep+'$1');
    s = _.join(dec);
} else {
    s = s.replace('.', dec);
}

var decPos = s.indexOf(dec);
if (prec >= 1 && decPos !== -1 && (s.length-decPos-1) < prec) {
    s += new Array(prec-(s.length-decPos-1)).join(0)+'0';
}
else if (prec >= 1 && decPos === -1) {
    s += dec+new Array(prec).join(0)+'0';
}
return s; }

http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/

DaMayan
+2  A: 

Below is the Daok code with a bit of comments added and some minor changes:

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 separetor is passed we use the comma as default decimal separator (we MUST use a decimal separator)

   /*
   according to [http://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 ot 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 http://stackoverflow.com/questions/411352/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

Marco Demajo
A: 
function getMoney(A){
    var a = new Number(A);
    var b = a.toFixed(2); //get 12345678.90
    a = parseInt(a); // get 12345678
    b = (b-a).toPrecision(2); //get 0.90
    b = parseFloat(b).toFixed(2); //in case we get 0.0, we pad it out to 0.00
    a = a.toLocaleString();//put in commas - IE also puts in .00, so we'll get 12,345,678.00
    //if IE (our number ends in .00)
    if(a.lastIndexOf('.00') == (a.length - 3))
    {
        a=a.substr(0, a.length-3); //delete the .00
    }
    return a+b.substr(1);//remove the 0 from b, then return a + b = 12,345,678.90
}
alert(getMoney(12345678.9));

This works in FF and IE

Dickie
A: 

As usually, there are multiple ways of doing the same thing but I would avoid using Number.prototype.toLocaleString since it can return different values based on the user settings.

I also don't recommend extending the Number.prototype - extending native objects prototypes is a bad practice since it can cause conflicts with other people code (e.g. libraries/frameworks/plugins) and may not be compatible with future JavaScript implementations/versions.

I believe that Regular Expressions are the best approach for the problem, here is my implementation:

/**
 * Converts number into currency format
 * @param {number} number   Number that should be converted.
 * @param {string} [decimalSeparator]    Decimal separator, defaults to '.'.
 * @param {string} [thousandsSeparator]    Thousands separator, defaults to ','.
 * @param {int} [nDecimalDigits]    Number of decimal digits, defaults to `2`.
 * @return {string} Formatted string (e.g. numberToCurrency(12345.67) returns '12,345.67')
 */
function numberToCurrency(number, decimalSeparator, thousandsSeparator, nDecimalDigits){
    //default values
    decimalSeparator = decimalSeparator || '.';
    thousandsSeparator = thousandsSeparator || ',';
    nDecimalDigits = nDecimalDigits || 2;

    var fixed = number.toFixed(nDecimalDigits), //limit/add decimal digits
        parts = RegExp('^(-?\\d{1,3})((\\d{3})+)\\.(\\d{'+ nDecimalDigits +'})$').exec( fixed ); //separate begin [$1], middle [$2] and decimal digits [$4]

    if(parts){ //number >= 1000 || number <= -1000
        return parts[1] + parts[2].replace(/\d{3}/g, thousandsSeparator + '$&') + decimalSeparator + parts[4];
    }else{
        return fixed.replace('.', decimalSeparator);
    }
}

edited on 2010/08/30: added option to set number of decimal digits.

Miller Medeiros