How to convert string representing a decimal using parseInt instead of parseFloat? - javascript

Given a string that represents a number use parseInt to convert string to number. I wrote a working function, but did not account for decimal numbers. Is there a way to convert decimals using parseInt? This is as far as I've gotten trying to account for decimals. The problem with this is NaN being returned. I can't think of a solution to implement that filters NaN from the results. The ultimate goal is to compare the two strings. My solution must use parseInt.
function convertStr(str1, str2) {
let num1 = str1.split('')
let num2 = str2.split('');
num1 = num1.map(str => parseInt(str));
num2 = num2.map(str => parseInt(str));
console.log(num1);
console.log(num2);
}
Any help is greatly appreciated.

I think this is a good step to what you are seeking for :
The question is : What do you want as output when encountering decimal value?
// Soluce to replace NaN by '.'
function convertStrReplace(str1) {
let num1 = str1.split('')
num1 = num1.map(str => parseInt(str)).map(x => isNaN(x) ? '.' : x);
console.log(num1);
}
// Soluce to ignore NaN
function convertStrIgnore(str1) {
let num1 = str1.split('')
num1 = num1.map(str => parseInt(str)).filter(x => !isNaN(x));
console.log(num1);
}
convertStrReplace('17,52');
convertStrIgnore('17,52');
Syntax alternative
function convertStrFilter(str1) {
const num1 = [
...str1,
].map(str => parseInt(str)).filter(x => !isNaN(x));
console.log(num1);
}
convertStrFilter('17,52');
Explaination about integer and string differences
// String and integer differences
// Put a number into a string
const str = '9000';
console.log(typeof str, str);
// Put a number into a number
const number = 9000;
console.log(typeof number, number);
// Compare both (compare value and type)
console.log('equality ===', str === number);
// Compare both (compare value)
console.log('equality ==', str == number);
const numberFromString = parseInt(str);
console.log(typeof numberFromString, numberFromString);
// Compare both (compare value and type)
console.log('equality ===', number === numberFromString);

Related

Javascript NumberFormat ends up with 'NaN' [duplicate]

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);
Yes remove the commas:
let output = parseFloat("2,299.00".replace(/,/g, ''));
console.log(output);
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:
https://github.com/google/closure-library/tree/master/closure/goog/i18n
https://github.com/globalizejs/globalize
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.
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">
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.
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, "."));
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.
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(...).
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]
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(''));
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
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, '.'));
};
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"
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, ""));
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, '.'));
}
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}`);
});
use this instead
const price = 1234567.89;
const formattedPrice = price.toLocaleString(); // "1,234,567.89"
to be more specific
const formattedPrice = price.toLocaleString('en-US', {style: 'currency', currency: 'USD'}); // "$1,234,567.89"

Convert a string to a big integer in Javascript?

I am trying to convert a string to a big integer to perform some arithmetic calculations. However, when I try this:
Number("9007199254740993")
...I am getting this unexpected result:
9007199254740992
I suspect that this is probably because of the limit on the size of integers that Number is capable of working with.
Basically, I want to check if two strings are consecutive numbers or not. Since Number is not returning the correct value, I am getting the incorrect difference for "9007199254740993" and "9007199254740992". Specifically, I am expecting 1, but getting 0.
One possibility I considered is dividing each number by a factor to make each of them smaller. Is there any other solution?
Javascript's Number type is a numeric data type in the double-precision 64-bit floating point format (IEEE 754).
If you are dealing with large integers, use a BigInt or a corresponding library.
I you don't want to rely on BigInt and only have positive integers in mind, you can also write the successor test yourself. Full code in the snippet below.
Notes
A string representation of a positive integer is easily convertible to a decimal array where the index represents the exponent to the base 10. For example "42" ~> [2, 4] (since 42 = 2*10^0 + 4*10^1). You can also just as easily convert it back.
Now for the successor test you just need to define the increment operation (which is just adding 1 with carry). With that you can just compare if the increment of one number is equal to the unincremented other number (and vice versa).
Code
// Convert a string representation of positive decimal integer to an array of decimals.
const toArray = numberString => Array.from(numberString, c => parseInt(c))
.reverse();
// Convert the array representation of a positive decimal integer string back to the corresponding string representation (this is the inverse of `toArray`).
const fromArray = numberArray => numberArray.map(String)
.reverse()
.join('');
console.log(fromArray(toArray("9007199254740993")) === "9007199254740993"); // true
// Perform the increment operation on the array representation of the positive decimal integer.
const increment = numberArray => {
let carry = 1;
const incrementedNumberArray = [];
numberArray.forEach(i => {
let j;
if (carry === 0) {
j = i;
} else if (carry === 1) {
if (i === 9) {
j = 0;
} else {
j = i + 1;
carry = 0;
}
}
incrementedNumberArray.push(j);
});
if (carry === 1) {
incrementedNumberArray.push(1);
}
return incrementedNumberArray;
};
console.log(fromArray(increment(toArray("9007199254740993"))) === "9007199254740994"); // true
console.log(fromArray(increment(toArray("9999999999999999"))) === "10000000000000000"); // true
// Test if two strings represent positive integers where one is the other's successor.
const isSuccessor = (a, b) => {
const a_ = increment(toArray(a));
const b_ = increment(toArray(b));
return fromArray(a_) === b || fromArray(b_) === a;
};
console.log(isSuccessor("9007199254740993", "9007199254740994")); // true
console.log(isSuccessor("9007199254740994", "9007199254740993")); // true
console.log(isSuccessor("9999999999999999", "10000000000000000")); // true
console.log(isSuccessor("10000000000000000", "9999999999999999")); // true
console.log(isSuccessor("10000000000000000", "10000000000000002")); // false
You can use BIG integer library like one in JAVA.
check here
npm install big-integer
var bigInt = require("big-integer");
var largeNumber1 = bigInt("9007199254740993");
var largeNumber2 = bigInt("9007199254740994"); // any other number
var ans = largeNumber1.minus(largeNumber2);
if(ans == 1 || ans == -1){
console.log('consecutive ')
}else{
console.log('not consecutive ')
}
Note: I recommend you to use BigInt(as suggested by #Andreas in comment), if you are dealing with Big Numbers.
UPDATED
Use this code to compare big positive integers(The arguments should be in string format)
function compareBigNumber(num1, num2) {
if (num1 > Number.MAX_SAFE_INTEGER && num2 > Number.MAX_SAFE_INTEGER) {
var newNum1 = num1.split('').reverse();
var newNum2 = num2.split('').reverse();
do {
newNum1.pop();
newNum2.pop();
} while (newNum1[newNum1.length-1] === '0' || newNum2[newNum2.length-1] === '0')
return compareBigNumber(newNum1.reverse().join(''), newNum2.reverse().join(''));
} else if(num1 > Number.MAX_SAFE_INTEGER){
return 'num1 is greater'
} else if (num2 > Number.MAX_SAFE_INTEGER) {
return 'num2 is greater'
}
else {
var num1Int = parseInt(num1);
var num2Int = parseInt(num2);
if (num1Int > num2Int) {
return 'Num1 is greater';
} else if (num2Int > num1Int){
return 'Num2 is greater'
} else {
return 'Num1 is equal to Num2';
}
}
}
console.log(compareBigNumber("9007199254740992", "9007199254740993"))
console.log(compareBigNumber("100000000000000000000", "0"))

How can I parse a string with a comma thousand separator to a number?

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);
Yes remove the commas:
let output = parseFloat("2,299.00".replace(/,/g, ''));
console.log(output);
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:
https://github.com/google/closure-library/tree/master/closure/goog/i18n
https://github.com/globalizejs/globalize
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.
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">
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.
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, "."));
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.
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(...).
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]
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(''));
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
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, '.'));
};
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"
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, ""));
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, '.'));
}
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}`);
});
use this instead
const price = 1234567.89;
const formattedPrice = price.toLocaleString(); // "1,234,567.89"
to be more specific
const formattedPrice = price.toLocaleString('en-US', {style: 'currency', currency: 'USD'}); // "$1,234,567.89"

Unexpected output in javascript

I am beginner to javascript and i am getting unexpected output
here is the code
<script type="text/javascript">
function add(a,b)
{
x = a+b;
return x;
}
var num1 = prompt("what is your no.");
var num2 = prompt("what is another no.")
alert(add(num1,num2));
</script>
it should give output as a sum of two number entered by us on prompting but it is simply concatenating the two number and popping the output
This is because the prompt function returns a String and not a Number. So what you're actually doing is to request 2 strings and then concatenate them. If you want to add the two numbers together you'll have to convert the strings to numbers:
var num1 = parseFloat(prompt("what is your no."));
var num2 = parseFloat(prompt("what is another no."));
or simpler:
var num1 = +prompt("what is your no.");
var num2 = +prompt("what is another no.");
prompt returns a string, not a number. + is used as both an addition and concatenation operator. Use parseInt to turn strings into numbers using a specified radix (number base), or parseFloat if they're meant to have a fractional part (parseFloat works only in decimal). E.g.:
var num1 = parseInt(prompt("what is your no."), 10);
// radix -----^
or
var num1 = parseFloat(prompt("what is your no."));
When you prompt the user, the return value is a string, normal text.
You should convert the strings in numbers:
alert(add(parseInt(num1), parseInt(num2));
The return value of prompt is a string. So your add function performs the + operator on 2 strings, thus concatenating them. Convert your inputs to int first to have the correct result.
function add(a,b)
{
x = parseInt( a ) + parseInt( b );
return x;
}
In addition to the already provided answers: If you're using parseInt() / parseFloat(), make sure to check if the input in fact was a valid integer or float:
function promptForFloat(caption) {
while (true) {
var f = parseFloat(prompt(caption));
if (isNaN(f)) {
alert('Please insert a valid number!');
} else {
return f;
}
}
}
var num1 = promptForFloat('what is your no.');
// ...

convert a JavaScript string variable to decimal/money

How can we convert a JavaScript string variable to decimal?
Is there a function such as:
parseInt(document.getElementById(amtid4).innerHTML)
Yes -- parseFloat.
parseFloat(document.getElementById(amtid4).innerHTML);
For formatting numbers, use toFixed:
var num = parseFloat(document.getElementById(amtid4).innerHTML).toFixed(2);
num is now a string with the number formatted with two decimal places.
You can also use the Number constructor/function (no need for a radix and usable for both integers and floats):
Number('09'); /=> 9
Number('09.0987'); /=> 9.0987
Alternatively like Andy E said in the comments you can use + for conversion
+'09'; /=> 9
+'09.0987'; /=> 9.0987
var formatter = new Intl.NumberFormat("ru", {
style: "currency",
currency: "GBP"
});
alert( formatter.format(1234.5) ); // 1 234,5 £
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
This works:
var num = parseFloat(document.getElementById(amtid4).innerHTML, 10).toFixed(2);
An easy short hand way would be to use +x
It keeps the sign intact as well as the decimal numbers.
The other alternative is to use parseFloat(x).
Difference between parseFloat(x) and +x is for a blank string +x returns 0 where as parseFloat(x) returns NaN.
It is fairly risky to rely on javascript functions to compare and play with numbers. In javascript (0.1+0.2 == 0.3) will return false due to rounding errors. Use the math.js library.
I made a little helper function to do this and catch all malformed data
const convertToPounds = (str = "", asNumber = false) => {
let num = Number.parseFloat(str);
if (isNaN(num) || num < 0) num = 0;
if (asNumber) return Math.round(num * 1e2) / 1e2
return num.toFixed(2);
};
Demo is here
Prefix + could be used to convert a string form of a number to a number (say "009" to 9)
const myNum = +"009"; // 9
But be careful if you want to SUM 2 or more number strings into one number.
const myNum = "001" + "009"; // "001009" (NOT 10)
const myNum = +"001" + +"009"; // 10
Alternatively you can do
const myNum = Number("001") + Number("009"); // 10

Categories