163

I have 2,299.00 as a string and I am trying to parse it to a number. I tried using parseFloat, which results in 2. I guess the comma is the problem, but how would I solve this issue the right way? Just remove the comma?

var x = parseFloat("2,299.00")
console.log(x);
Zsolt Meszaros
  • 21,961
  • 19
  • 54
  • 57
user1540714
  • 1,929
  • 4
  • 20
  • 23

16 Answers16

190

Yes remove the commas:

let output = parseFloat("2,299.00".replace(/,/g, ''));
console.log(output);
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Sam
  • 2,264
  • 1
  • 13
  • 2
  • 7
    Yeah, but now the decimal places are lost. 2300.00 results in 2300 for example. – user1540714 Jul 26 '12 at 09:18
  • 1
    @user1540714 thats because its a float rather than a string. If you then need to output it you need to format it to always show 2 decimal points. – Jon Taylor Jul 26 '12 at 09:19
  • Can that be avoided? Ill try toFixed – user1540714 Jul 26 '12 at 09:19
  • 4
    In french locale, a comma is a decimal separator... So this fails for a very likely scenario that a browser has set french locale – Aqeel Ashiq Sep 27 '17 at 12:51
  • If you are getting the original figure from a database, json encoded data or other data source as a string, am I right in thinking it won't affected by browser locale until it's converted to a number? This would mean you can carry out string operations as suggested above to remove commas, although if you then use `parseFloat()` on the string it would depend on locale. One way round the locale issues could be to split the number on the decimal point and output as `(part1 + part2/100).toFixed(2)` – Jon Mar 01 '18 at 12:21
  • In ECMAScript 2021 we can use [`.replaceAll(",", "")`](//developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll). – Sebastian Simon Jan 05 '22 at 14:16
182

Removing commas is potentially dangerous because, as others have mentioned in the comments, many locales use a comma to mean something different (like a decimal place).

I don't know where you got your string from, but in some places in the world "2,299.00" = 2.299

The Intl object could have been a nice way to tackle this problem, but somehow they managed to ship the spec with only a Intl.NumberFormat.format() API and no parse counterpart :(

The only way to parse a string with cultural numeric characters in it to a machine recognisable number in any i18n sane way is to use a library that leverages CLDR data to cover off all possible ways of formatting number strings http://cldr.unicode.org/

The two best JS options I've come across for this so far:

David Meister
  • 3,941
  • 1
  • 26
  • 27
  • 6
    Can't beleive no one upvoted this answer yet, it's the only actual answer on this page! – evilkos Aug 08 '17 at 17:56
  • 19
    Completely agree that Intl should've had a parsing counterpart. It seems obvious that people would need this. – carlossless Dec 11 '17 at 11:54
  • 1
    This is the only systematic way to do this. Can't achieve this with one regex fits-all approach. Commas and periods have different meanings in different languages. – Wildhammer Jun 11 '20 at 19:44
  • 2
    Actually, nowhere in the world would `"2,299.00" == 2.299`. Any sane human would look at that number and assume it is `2299` formatted to two decimals in a format that uses commas for grouping and periods/full stops for decimals. Unless you're telling me there's a place where they group decimal fractions using periods and stop after 1 2/3 groups? I call BS. In any case, you can use the formatting rules exposed by `Intl.NumberFormat` to build a parser: see the answers to [Is there any JavaScript standard API to parse to number according to locale?](https://stackoverflow.com/q/55364947/215552) – Heretic Monkey May 08 '22 at 23:39
  • @HereticMonkey `gl` locale appears to work as i explained according to my tests with cldr definitions - https://github.com/thedavidmeister/cljs-i18n/blob/master/src/i18n/number.cljs#L240 – David Meister May 12 '22 at 11:22
  • @DavidMeister I'm talking about humans, not edge cases for obscure computer "locales" used by a couple million people at most (who are also likely fluent with Spanish and/or Portuguese standards). Sure, CLDR has its place, but pushing libraries you work on (i.e., Closure) is inappropriate. – Heretic Monkey May 12 '22 at 13:55
  • @HereticMonkey i don't work on closure, that's google's library, CLDR has parsing rules exactly as i stated in my answer, the test i linked to is a test that uses CLDR and google's library – David Meister May 29 '22 at 15:55
64

On modern browsers you can use the built in Intl.NumberFormat to detect the browser's number formatting and normalize the input to match.

function parseNumber(value, locales = navigator.languages) {
  const example = Intl.NumberFormat(locales).format('1.1');
  const cleanPattern = new RegExp(`[^-+0-9${ example.charAt( 1 ) }]`, 'g');
  const cleaned = value.replace(cleanPattern, '');
  const normalized = cleaned.replace(example.charAt(1), '.');

  return parseFloat(normalized);
}

const corpus = {
  '1.123': {
    expected: 1.123,
    locale: 'en-US'
  },
  '1,123': {
    expected: 1123,
    locale: 'en-US'
  },
  '2.123': {
    expected: 2123,
    locale: 'fr-FR'
  },
  '2,123': {
    expected: 2.123,
    locale: 'fr-FR'
  },
}


for (const candidate in corpus) {
  const {
    locale,
    expected
  } = corpus[candidate];
  const parsed = parseNumber(candidate, locale);

  console.log(`${ candidate } in ${ corpus[ candidate ].locale } == ${ expected }? ${ parsed === expected }`);
}

Their's obviously room for some optimization and caching but this works reliably in all languages.

Paul Alexander
  • 31,970
  • 14
  • 96
  • 151
  • 1
    Why has this not got a load of upvotes!!! It is the most elegant solution to INPUT in international formats! Thank you!! – kpollock Aug 23 '17 at 14:39
  • in France currency is 231 123 413,12 – stackdave Nov 29 '17 at 19:16
  • If you need support for IE11, replace 3rd line to: `const cleanPattern = new RegExp("[^-+0-9" + example.charAt( 1 ) + "]", 'g');` - template strings "`" are not supported by IE - https://caniuse.com/#search=template%20string – long Sep 04 '20 at 14:10
  • Isn't it better to pass `navigator.languages` instead of `navigator.language` to the constructor? I'm wondering because in the book "Modern JavaScript for the impatient" p.180, it suggests using that one. Not sure what the difference is though – Kohei Nozaki Oct 08 '20 at 03:23
  • @KoheiNozaki, this function can take any locale as an argument, so you could in theory format your numbers multiple ways on the same page. navigator.language is used as a sensible default because it _should_ be the preferred language of the user. – Paul Alexander Oct 09 '20 at 17:00
  • @PaulAlexander Yeah I knew that, I was just wondering if `navigator.languages` is better than `navigator.language` for the default value and what the difference is between the two in terms of the behavior of the parseNumber function – Kohei Nozaki Oct 14 '20 at 00:45
  • 1
    @KoheiNozaki I took another look at [Locale negotiation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_identification_and_negotiation) and it seems you're on to something. The browser should negotiate the preferred locale if you pass all of them in so it shouldn't hurt and could be better to pass them all in. – Paul Alexander Oct 15 '20 at 02:12
  • This always assumes the decimal separator is always 1 character long. A slightly modification to this might be: `const decimal = Intl.NumberFormat(locales).formatToParts('1.1').find(part => part.type === 'decimal').value ` and `const cleanPattern = new RegExp(\`[^-+0-9${ decimal }]\`, 'g');` – prageeths Apr 11 '21 at 15:24
  • 1
    `parseNumber("12,00")` will return `1200` assuming an `en-US` local. how can this be enhanced to return empty string when the string is not correct? – davidtingsu Jul 20 '21 at 00:50
  • This should be the accepted answer. Great method :) – zreptil Oct 14 '21 at 10:46
28

Caveat: This won't work for numbers in scientific notation (like 1e3 for one thousand).

Remove anything that isn't a digit, decimal separator, or minus sign (-) (or optionally, a + if you want to allow a unary + on the number).

If you can assume that . is the decimal separator (it isn't in many parts of the world; keep reading), that might look like this:

function convertToFloat(str) {
    let body = str;
    let sign = "";
    const signMatch = /^\s*(-|\+)/.exec(str);
    // Or if you don't want to support unary +:
    // const signMatch = /^\s*(-)/.exec(str);
    if (signMatch) {
        body = str.substring(signMatch.index + 1);
        sign = signMatch[1];
    }
    const updatedBody = str.replace(/[^\d\.]/g, "");
    const num = parseFloat(sign + updatedBody);
    return num;
}

Live Example (I've added a fractional portion to the number just to show that working):

function convertToFloat(str) {
    let body = str;
    let sign = "";
    const signMatch = /^\s*(-|\+)/.exec(str);
    // Or if you don't want to support unary +:
    // const signMatch = /^\s*(-)/.exec(str);
    if (signMatch) {
        body = str.substring(signMatch.index + 1);
        sign = signMatch[1];
    }
    const updatedBody = str.replace(/[^\d\.]/g, "");
    const num = parseFloat(sign + updatedBody);
    return num;
}

console.log(convertToFloat("2,299.23"));

If you want to support locales where . isn't the decimal separator (there are many), you can detect the decimal separator and use the detected one in your regular expression. Here's an example function for finding the decimal separator:

function findDecimalSeparator() {
    const num = 1.2;
    if (typeof Intl === "object" && Intl && Intl.NumberFormat) {
        // I'm surprised it's this much of a pain and am hoping I'm missing
        // something in the API
        const formatter = new Intl.NumberFormat();
        const parts = formatter.formatToParts(num);
        const decimal = parts.find(({ type }) => type === "decimal").value;
        return decimal;
    }
    // Doesn't support `Intl.NumberFormat`, fall back to dodgy means
    const str = num.toLocaleString();
    const parts = /1(\D+)2/.exec(str);
    return parts[1];
}

Then convertToFloat looks like:

const decimal = findDecimalSeparator();
function convertToFloat(str) {
    let body = str;
    let sign = "";
    const signMatch = /^\s*(-|\+)/.exec(str);
    // Or if you don't want to support unary +:
    // const signMatch = /^\s*(-)/.exec(str);
    if (signMatch) {
        body = str.substring(signMatch.index + 1);
        sign = signMatch[1];
    }
    const rex = new RegExp(`${escapeRegex(decimal)}|-|\\+|\\D`, "g");
    const updatedBody = body.replace(
        rex,
        (match) => match === decimal ? "." : ""
    );
    const num = parseFloat(sign + updatedBody);
    return num;
}

Live Example:

const decimal = findDecimalSeparator();

function findDecimalSeparator() {
    const num = 1.2;
    if (typeof Intl === "object" && Intl && Intl.NumberFormat) {
        // I'm surprised it's this much of a pain and am hoping I'm missing
        // something in the API
        const formatter = new Intl.NumberFormat();
        const parts = formatter.formatToParts(num);
        const decimal = parts.find(({ type }) => type === "decimal").value;
        return decimal;
    }
    // Doesn't support `Intl.NumberFormat`, fall back to dodgy means
    const str = num.toLocaleString();
    const parts = /1(\D+)2/.exec(str);
    return parts[1];
}

function escapeRegex(string) {
    return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
}

function convertToFloat(str) {
    let body = str;
    let sign = "";
    const signMatch = /^\s*(-|\+)/.exec(str);
    // Or if you don't want to support unary +:
    // const signMatch = /^\s*(-)/.exec(str);
    if (signMatch) {
        body = str.substring(signMatch.index + 1);
        sign = signMatch[1];
    }
    const rex = new RegExp(`${escapeRegex(decimal)}|-|\\+|\\D`, "g");
    const updatedBody = body.replace(
        rex,
        (match) => match === decimal ? "." : ""
    );
    const num = parseFloat(sign + updatedBody);
    return num;
}

function gid(id) {
    const element = document.getElementById(id);
    if (!element) {
        throw new Error(`No element found for ID ${JSON.stringify(id)}`);
    }
    return element;
}

function onClick(id, handler) {
    gid(id).addEventListener("click", handler);
}

onClick("convert", () => {
    const str = gid("num").value;
    const num = convertToFloat(str);
    console.log(`${JSON.stringify(str)} => ${num}`);
});
<div>Enter a number using your locale's grouping and decimal separators, optionally prefaced with a minus sign (<code>-</code>) or plus sign (<code>+</code>):</div>
<input type="text" id="num" value="-123">
<input type="button" id="convert" value="Convert">
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 4
    This won't work for negative numbers or numbers in scientific notation. – Aadit M Shah Jul 26 '12 at 09:09
  • 1
    `str = str.replace(/(\d+),(?=\d{3}(\D|$))/g, "$1");` This is what I would use but I'm utterly useless at regex and is something I found a while back on some other SO thread. – Jon Taylor Jul 26 '12 at 09:13
  • @JonTaylor: The goal here wasn't to *validate* the number, just to make it work for `parseFloat` -- which will validate it. :-) – T.J. Crowder Jul 26 '12 at 09:14
  • which mine does do, as far as I know. I use this to convert 1,234,567 etc to 1234567. As I said though I'm utterly useless at regex so I couldn't for the life of me tell you what it actually does lol. – Jon Taylor Jul 26 '12 at 09:16
  • 1
    bad idea to remove "-" minus sign - obtain completly other number, and also if you parse heterogenous formats, "7.500" !== "7,500" – serge Apr 10 '20 at 19:12
  • @Serge - The above **doesn't** remove the `-`, it keeps it. The OP didn't talk about heterogeneous formats, just `,` as a grouping separator. – T.J. Crowder Apr 11 '20 at 09:06
  • The above solution works for me. I think its the best accurate way to remove 1000 separators. Thanks @T.J.Crowder – Ansif Jul 07 '21 at 05:10
  • Ho wise TJ, I implore you to conjure an updated answer which takes into account other possible [locales](https://en.wikipedia.org/wiki/Decimal_separator#Examples_of_use) – vsync Jan 06 '23 at 15:00
  • @vsync - At that point, we're pretty far afield of the question as asked, but it was bothering me, so I added it. :-) – T.J. Crowder Jan 06 '23 at 17:15
  • I don't think relying on the locale alone is sufficient, as it would not be unfrequent for someone to use the US English locale, despite the website or the user being from somewhere different, and thus used to using the comma separator. Not sure what the right way to go would be. Maybe the distinction would only matter if you know the comma is used before a group of three numbers and then it would be a thousands separator (still breaking for more than two decimals of course – Haroen Viaene May 02 '23 at 09:48
18

Usually you should consider to use input fields which don't allow free text input for numeric values. But there might be cases, when you need to guess the input format. For example 1.234,56 in Germany means 1,234.56 in US. See https://salesforce.stackexchange.com/a/21404 for a list of countries which use comma as decimal.

I use the following function to do a best guess and strip off all non-numeric characters:

function parseNumber(strg) {
    var strg = strg || "";
    var decimal = '.';
    strg = strg.replace(/[^0-9$.,]/g, '');
    if(strg.indexOf(',') > strg.indexOf('.')) decimal = ',';
    if((strg.match(new RegExp("\\" + decimal,"g")) || []).length > 1) decimal="";
    if (decimal != "" && (strg.length - strg.indexOf(decimal) - 1 == 3) && strg.indexOf("0" + decimal)!==0) decimal = "";
    strg = strg.replace(new RegExp("[^0-9$" + decimal + "]","g"), "");
    strg = strg.replace(',', '.');
    return parseFloat(strg);
}   

Try it here: https://plnkr.co/edit/9p5Y6H?p=preview

Examples:

1.234,56 € => 1234.56
1,234.56USD => 1234.56
1,234,567€ => 1234567
1.234.567 => 1234567
1,234.567 => 1234.567
1.234 => 1234 // might be wrong - best guess
1,234 => 1234 // might be wrong - best guess
1.2345 => 1.2345
0,123 => 0.123

The function has one weak point: It is not possible to guess the format if you have 1,123 or 1.123 - because depending on the locale format both might be a comma or a thousands-separator. In this special case the function will treat separator as a thousands-separator and return 1123.

ESP32
  • 8,089
  • 2
  • 40
  • 61
  • It fails for numbers like 1,111.11, which is obviously english format, but returns 111111 – Mr. Goferito Apr 20 '17 at 18:16
  • Thank you, Mr. Goferito - I am sorry - I fixed the function. – ESP32 Apr 21 '17 at 19:47
  • Looks like this may also fail for very small numbers "0,124" in french locale for example. – Paul Alexander Jul 23 '17 at 04:52
  • Wou, great! This is almost exactly, what I was looking for: `3,00 €` also is replaced to 3.00. Just a note, that `3,001` is formatted to `3001`. To avoid this, input should always be with decimal symbols. E.g. `3,001.00€` `3,001.00` converts correctly. Also, please, update jsfiddle. `0,124` there is still converted to 124 – Arnis Juraga Oct 19 '17 at 15:45
  • well done buddy I think its worth to be the correct answer – Waheed Nov 01 '17 at 21:55
  • Great function. I would suggest an additional check in the 3rd if statement. `strg.indexOf(decimal) !== 0`. This will catch the .123 and ,123 cases and treat them the same as the 0.123 and 0,123 cases. – sunnymtn Jun 21 '18 at 17:50
5

It's baffling that they included a toLocaleString but not a parse method. At least toLocaleString without arguments is well supported in IE6+.

For a i18n solution, I came up with this:

First detect the user's locale decimal separator:

var decimalSeparator = 1.1;
decimalSeparator = decimalSeparator.toLocaleString().substring(1, 2);

Then normalize the number if there's more than one decimal separator in the String:

var pattern = "([" + decimalSeparator + "])(?=.*\\1)";separator
var formatted = valor.replace(new RegExp(pattern, "g"), "");

Finally, remove anything that is not a number or a decimal separator:

formatted = formatted.replace(new RegExp("[^0-9" + decimalSeparator + "]", "g"), '');
return Number(formatted.replace(decimalSeparator, "."));
Fábio
  • 3,291
  • 5
  • 36
  • 49
4
Number("2,299.00".split(',').join(''));   // 2299

The split function splits the string into an array using "," as a separator and returns an array.
The join function joins the elements of the array returned from the split function.
The Number() function converts the joined string to a number.

BrunoElo
  • 360
  • 6
  • 11
3

If you want to avoid the problem that David Meister posted and you are sure about the number of decimal places, you can replace all dots and commas and divide by 100, ex.:

var value = "2,299.00";
var amount = parseFloat(value.replace(/"|\,|\./g, ''))/100;

or if you have 3 decimals

var value = "2,299.001";
var amount = parseFloat(value.replace(/"|\,|\./g, ''))/1000;

It's up to you if you want to use parseInt, parseFloat or Number. Also If you want to keep the number of decimal places you can use the function .toFixed(...).

3

or try this shorter approach:

const myNum =  +('2,299.00'.replace(",",""));

If you have several commas use Regex:

const myNum =  +('2,022,233,988.55'.replace(/,/g,""));
// -> myNum = 2022233988.55

Here was my case in an array (for similar use case):

To get the sum of this array:

const numbers = ["11", "7", "15/25", "18/5", "12", "16/25"]

By using parseFloat I would lose the decimals so to get the exact sum I had to first replace the forward slash with dot, then convert the strings to actual numbers.

So:

const currectNumbers = numbers.map(num => +(num.replace("/",".")))

// or the longer approach:
const currectNumbers = numbers
.map(num => num.replace("/","."))
.map(num => parseFloat(num));

This will give me the desired array to be used in reduce method:

currectNumbers = [ 11, 7, 15.25, 18.5, 12, 16.25]
e.saleh
  • 134
  • 2
  • 2
  • 8
2

All of these answers fail if you have a number in the millions.

3,456,789 would simply return 3456 with the replace method.

The most correct answer for simply removing the commas would have to be.

var number = '3,456,789.12';
number.split(',').join('');
/* number now equips 3456789.12 */
parseFloat(number);

Or simply written.

number = parseFloat(number.split(',').join(''));
Case
  • 4,244
  • 5
  • 35
  • 53
  • Of course, American's use comma and not dot. It would be foolish to try to make a monstrosity to attempt to handle both. – Case May 23 '17 at 18:45
  • 3
    but such a "monstrosity" already exists as part of what Unicode provides (see my answer). I'm sure you would feel less foolish about this if you ran a company with international customers. – David Meister May 25 '17 at 03:40
2

This converts a number in whatever locale to normal number. Works for decimals points too:

function numberFromLocaleString(stringValue, locale){
    var parts = Number(1111.11).toLocaleString(locale).replace(/\d+/g,'').split('');
    if (stringValue === null)
        return null;
    if (parts.length==1) {
        parts.unshift('');
    }   
    return Number(String(stringValue).replace(new RegExp(parts[0].replace(/\s/g,' '),'g'), '').replace(parts[1],"."));
}
//Use default browser locale
numberFromLocaleString("1,223,333.567") //1223333.567

//Use specific locale
numberFromLocaleString("1 223 333,567", "ru") //1223333.567
1
const parseLocaleNumber = strNum => {
    const decSep = (1.1).toLocaleString().substring(1, 2);
    const formatted = strNum
        .replace(new RegExp(`([${decSep}])(?=.*\\1)`, 'g'), '')
        .replace(new RegExp(`[^0-9${decSep}]`, 'g'), '');
    return Number(formatted.replace(decSep, '.'));
};
Denys Rusov
  • 560
  • 6
  • 6
1

With this function you will be able to format values in multiple formats like 1.234,56 and 1,234.56, and even with errors like 1.234.56 and 1,234,56

/**
 * @param {string} value: value to convert
 * @param {bool} coerce: force float return or NaN
 */
function parseFloatFromString(value, coerce) {
    value = String(value).trim();

    if ('' === value) {
        return value;
    }

    // check if the string can be converted to float as-is
    var parsed = parseFloat(value);
    if (String(parsed) === value) {
        return fixDecimals(parsed, 2);
    }

    // replace arabic numbers by latin
    value = value
    // arabic
    .replace(/[\u0660-\u0669]/g, function(d) {
        return d.charCodeAt(0) - 1632;
    })

    // persian
    .replace(/[\u06F0-\u06F9]/g, function(d) {
        return d.charCodeAt(0) - 1776;
    });

    // remove all non-digit characters
    var split = value.split(/[^\dE-]+/);

    if (1 === split.length) {
        // there's no decimal part
        return fixDecimals(parseFloat(value), 2);
    }

    for (var i = 0; i < split.length; i++) {
        if ('' === split[i]) {
            return coerce ? fixDecimals(parseFloat(0), 2) : NaN;
        }
    }

    // use the last part as decimal
    var decimal = split.pop();

    // reconstruct the number using dot as decimal separator
    return fixDecimals(parseFloat(split.join('') +  '.' + decimal), 2);
}

function fixDecimals(num, precision) {
    return (Math.floor(num * 100) / 100).toFixed(precision);
}
parseFloatFromString('1.234,56')
"1234.56"
parseFloatFromString('1,234.56')
"1234.56"
parseFloatFromString('1.234.56')
"1234.56"
parseFloatFromString('1,234,56')
"1234.56"
joseantgv
  • 1,943
  • 1
  • 26
  • 34
1

Based on many great architects here, I've simplified it a bit.

I prefer to use Intl.NumberFormat(undefined) to make it use the best fit mechanism.

If the user, like me, has a Danish keyboard, but prefer the Mac to be english, this helps: if (Number.isNaN(normalized)) return Number(value.replace(',', '.'));

If this is used in a form, I found that I should use inputMode="numeric" rather than type="number".

function parseNumber(value, locales = undefined) {
  if (typeof value !== 'string') return value;
  const example = Intl.NumberFormat(locales).format('1.1');
  const normalized = Number(value.replace(example.charAt(1), '.'));
  if (Number.isNaN(normalized)) return Number(value.replace(',', '.'));
  return normalized;
}

/* test */

const tests = [
  {
    locale: 'en-US',
    candidate: 1.123,
    expected: 1.123,
  },
  {
    locale: 'en-US',
    candidate: '1.123',
    expected: 1.123,
  },
  {
    locale: 'fr-FR',
    candidate: '33.123',
    expected: 33.123,
  },
  {
    locale: 'fr-FR',
    candidate: '33,123',
    expected: 33.123,
  },
  {
    locale: 'da-DK',
    candidate: '45.123',
    expected: 45.123,
  },
  {
    locale: 'da-DK',
    candidate: '45,123',
    expected: 45.123,
  },
  {
    locale: 'en-US',
    candidate: '0.123',
    expected: 0.123,
  },
  {
    locale: undefined,
    candidate: '0,123',
    expected: 0.123,
  },
];

tests.forEach(({ locale, candidate, expected }) => {
  const parsed = parseNumber(candidate, locale);
  console.log(`${candidate} as ${typeof candidate} in ${locale}: ${parsed} === ${expected}? ${parsed === expected}`);
});
webjay
  • 5,358
  • 9
  • 45
  • 62
0

If you want a l10n answer do it this way. Example uses currency, but you don't need that. Intl library will need to be polyfilled if you have to support older browsers.

var value = "2,299.00";
var currencyId = "USD";
var nf = new Intl.NumberFormat(undefined, {style:'currency', currency: currencyId, minimumFractionDigits: 2});

value = nf.format(value.replace(/,/g, ""));
Tony Topper
  • 402
  • 6
  • 14
0

If you have a small set of locales to support you'd probably be better off by just hardcoding a couple of simple rules:

function parseNumber(str, locale) {
  let radix = ',';
  if (locale.match(/(en|th)([-_].+)?/)) {
    radix = '.';
  }
  return Number(str
    .replace(new RegExp('[^\\d\\' + radix + ']', 'g'), '')
    .replace(radix, '.'));
}
Andreas Baumgart
  • 2,647
  • 1
  • 25
  • 20