I had a requirement to group digits (adding thousands separators) on both sides of the decimal point (the whole and the fractional parts) using javascript. I investigated whether this would be possible without too much complexity using regex.
The solutions available online seem to be focused on grouping whole numbers or only to the left of the decimal point, so I tried to come up with my own solution, including a validation regex...
The code in the snippet only tests using the given validation regex, so if it is not sound, the test results would be invalid...
function addThousandsSeparators(n) {
return n.toString().replace(/-?\d+?(?=(?:\d{3})+(?:\D|$))/gy, '$&,').replace(/\d{3}(?=\d)/g, '$&,')
}
function validateThousandsSeparators(n) {
return /^-?\d{1,3}(?:,\d{3})*(?:\.(?:\d{3},)*\d{1,3})?$/.test(n)
}
var tests = [
0,
1,
1.1,
1.0123456789,
-10.012345678,
210.01234567,
-3210.0123456,
43210.012345,
-543210.01234,
6543210.0123,
-76543210.012,
876543210.01,
-9876543210.1,
1,
10,
-210,
-3210,
43210,
543210,
-6543210,
-76543210,
876543210,
9876543210,
0.0123456789,
.012345678,
-0.01234567,
-.0123456,
0.012345,
.01234,
-0.0123,
-.012,
0.01,
.1
]
, i
function test(n) {
n = addThousandsSeparators(n)
console.log(
(
validateThousandsSeparators(n) ?
'valid: ' : 'invalid: '
) + n
)
}
for(i of tests)
test(i)
Brief
Please note that the output is what the OP expects. The output's decimal points should mirror the whole number separators such that 1234.1234 becomes 1,234.123,4 and not 1,234.1,234. For this opposite effect (format of 1,234.1,234), please expand and run the snippet directly below.
var nums = [ 0, 1, 1.1, 1.0123456789, -10.012345678, 210.01234567, -3210.0123456, 43210.012345, -543210.01234, 6543210.0123, -76543210.012, 876543210.01, -9876543210.1, 1, 10, -210, -3210, 43210, 543210, -6543210, -76543210, 876543210, 9876543210, 0.0123456789, .012345678, -0.01234567, -.0123456, 0.012345, .01234, -0.0123, -.012, 0.01, .1 ];
nums.forEach(function(n){
console.log(addSeparators(n));
});
function addSeparators(n) {
var regex = /\d{3}(?![-.]|$)/g;
var replace = `$&,`;
n = n.toString();
n = reverseNumber(n).replace(regex, replace);
return reverseNumber(n);
}
function reverseNumber(n) {
return n.split("").reverse().join("");
}
Code
The code below provides a simple regex and method to adding separators. The string is first reversed to add separators to the whole number part, then reversed once again to add separators to the decimal number part.
var nums = [ 0, 1, 1.1, 1.0123456789, -10.012345678, 210.01234567, -3210.0123456, 43210.012345, -543210.01234, 6543210.0123, -76543210.012, 876543210.01, -9876543210.1, 1, 10, -210, -3210, 43210, 543210, -6543210, -76543210, 876543210, 9876543210, 0.0123456789, .012345678, -0.01234567, -.0123456, 0.012345, .01234, -0.0123, -.012, 0.01, .1 ];
nums.forEach(function(n){
console.log(addSeparators(n));
});
function addSeparators(n) {
var regex = /\d{3}(?=[^.,]+$)(?!-)/g;
var replace = `$&,`;
n = n.toString();
n = reverseNumber(n).replace(regex, replace);
n = reverseNumber(n).replace(regex, replace);
return n;
}
function reverseNumber(n) {
return n.split("").reverse().join("");
}
Explanation
\d{3} Match a digit exactly 3 times
(?=[^.,]+$) Ensure no decimal points or separators exist between here and the end of the line (prevents duplicate separators and ensures it's only manipulating the part of the string after the .)
(?!-) Ensure what follows is not the negative sign (this prevents, for example, -543210.01234 from becoming -,543,210.012,34)
Related
Generate string from integer with arbitrary base in JavaScript received the following answer:
function parseInt(value, code) {
return [...value].reduce((r, a) => r * code.length + code.indexOf(a), 0);
}
function toString(value, code) {
var digit,
radix= code.length,
result = '';
do {
digit = value % radix;
result = code[digit] + result;
value = Math.floor(value / radix);
} while (value)
return result;
}
console.log(parseInt('dj', 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
console.log(toString(123, 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
console.log(parseInt('a', 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
console.log(toString(0, 'abcdefghijklmnopqrstuvwxyz0123456789+-'));
I am interested something slightly different. Whereas this will generate the shortest code for the number, I would like to now generate a constant-length code based on the number of bits. I am not sure if this is also a complex radix solution as well.
Say I want to generate 8-bit codes using a 16-character alphabet. That means I should be able to take the first 4 bits to select 1 character, and the next 4 bits to select the second character. So I might end up with MV if my 16 character set was ABDHNMOPQRSTUVYZ. Likewise if I had a 16-bit range, I would have 4 character code, and 32-bit range would be an 8-character code. So calling code32(1, 'ABDHNMOPQRSTUVYZ') would give an 8 letter code, while code8(1, 'ABDHNMOPQRSTUVYZ') would give a 2 digit code.
How could that be implemented in JavaScript? Something along these lines?
code8(i, alpha) // 0 to 255 it accepts
code16(i, alpha) // 0 to 65535 it accepts
code32(i, alpha) // 0 to 2^32-1 it accepts
Likewise, how would you get the string code back into the original number (or bit sequence)?
This really comes down to changing toString so that:
It only accepts a code that has a length of a power of 2
It pads the result to a given number of "digits" (characters)
The actual number of digits you would use for a 16 bit number depends on the size of the code. If the code has 16 characters, then it can cover for 4 bits, and so an output of 4 characters would be needed. If however the code has 4 characters, then the output would need 8 characters. You can have cases where the match is not exact, like when you would have a code with 8 characters. Then the output would need 6 characters.
Here I have highlighted the changes to the toString method. My personal preference is to also put the value as last parameter to toString.
function toString(digitCount, code, value) { // <-- add argument digitCount
// Perform a sanity check: code must have a length that is power of 2
if (Math.log2(code.length) % 1) throw "code size not power of 2: " + code.length;
var digit,
radix = code.length,
result = '';
do {
digit = value % radix;
result = code[digit] + result;
value = Math.floor(value / radix);
} while (value)
return result.padStart(digitCount, code[0]); // Pad to the desired output size
}
console.log(toString(4, 'abcdefghijklmnop', 123));
console.log(toString(4, 'abcdefghijklmnop', 0));
console.log(toString(4, 'abcdefghijklmnop', 0xFFFF));
// You could define some more specific functions
const code8 = (code, value) => toString(Math.ceil(8 / Math.log2(code.length)), code, value);
const code16 = (code, value) => toString(Math.ceil(16 / Math.log2(code.length)), code, value);
console.log(code16('abcdefghijklmnop', 123));
console.log(code16('abcdefghijklmnop', 0));
console.log(code16('abcdefghijklmnop', 0xFFFF));
console.log(code8('abcdefghijklmnop', 123));
console.log(code8('abcdefghijklmnop', 0));
console.log(code8('abcdefghijklmnop', 0xFF));
EDIT: I just noticed that you required a decoder as well. It is easy to implement a non-optimal version too, while an optimal one can be implemented via go through each letter and accumulate their value times their weighs.
Is this what you want? I tested this code for bit=16 and bit=8, but when bit=32 the count of codewords becomes too large and hangs the devtools of the browser. It's only a demonstrative code and may need optimization if need to be applied in practical use...
function genCode(len, alpha){
let tmp = [...alpha];
for(let i = 1; i != len; ++i){
const ttmp = [];
tmp.forEach(te => {
[...alpha].forEach(e => {
ttmp.push(te + e);
});
});
tmp = ttmp;
}
return tmp;
}
function code(bits, i, alpha){
const len = Math.ceil(bits / Math.floor(Math.log2(alpha.length)));
return genCode(len, alpha)[i];
}
function decode(bits, c, alpha){
const len = Math.ceil(bits / Math.floor(Math.log2(alpha.length)));
const codes = genCode(len, alpha);
return codes.indexOf(c);
}
console.log(code(16, 2, "ABDHNMOPQRSTUVYZ"));
console.log(decode(16, "AAAD", "ABDHNMOPQRSTUVYZ"));
console.log(code(8, 255, "ABDHNMOPQRSTUVYZ"));
console.log(decode(8, "ZZ", "ABDHNMOPQRSTUVYZ"));
If I have a variable that is set to 2345, and I want to convert it to exponential, I simply do variableName.toExponential().replace(/e+?/, 'e'), which will give me 2.345e3. However, I want it to only return two decimal places there, because otherwise once I get to much larger numbers, like 183947122, I'll get a long decimal, 1.83947122e8. I want this to floor to 1.83e8, but I can't figure out where I would put variable.toFixed(2) in this code.
var a=1233434;
console.log(a.toExponential(2));
you can pass the parameter in .toExponential(2) function for rounding.it will give 2 number after decimal check this link https://www.geeksforgeeks.org/javascript-toexponential-function/
You could calculate the floored value and then apply toExponential.
const f = (x, p) => {
const l = 10 ** (Math.floor(Math.log10(Math.abs(x))) - p);
return (Math.floor(x / l) * l).toExponential(p);
}
console.log(f(183947122, 2));
console.log(f(-183947122, 2));
console.log(f(183947122, 4));
You can do it with a regular expression and replace (which you're already using to replace e+ with e):
const str = variableName.toExponential().replace(/^(\d*\.\d{0,2})\d*e\+(\d+)$/, "$1e$2");
That captures the whole number portion of the coefficient plus up to two fractional digits of it, ignores any others, and captures the full exponent; it replaces the match with $1$2 so you're left with just the up-to-two digits of the coefficient:
function test(variableName) {
const raw = variableName.toExponential();
const str = raw.replace(/^(\d*(?:\.\d{0,2})?)\d*e\+(\d+)$/, "$1e$2");
console.log(variableName, "=>", raw, "=>", str);
}
test(2345);
test(100);
test(1019);
test(183947122);
I have simple function that calculates number of decimals,
eg. _d(0.01) = 2, _d(0.001) = 3 and so on.
We added some new coins to our system that have 0.00000001 quantity and function broke.
Here is why:
0.00000001.toString() = 1e-8, so I cant split it it by '.' and calculate length of second part as I did before.
So the question is - how to get string '0.00000001' out of 0.00000001 number easiest way.
EDIT
I didnt mean exactly '0.00000001', I meant any micronumber to decimal without exp. Some function _d(x) that would work _d(0.000000000012) = '0.000000000012'and so on. What usually toString() does to large (but not too large) numbers.
Use toFixed() with a large number of digits, then count the number of zeroes after the decimal point.
function _d(num) {
var str = num.toFixed(100);
var fraction = str.split('.')[1];
var zeros = fraction.match(/^0*/)[0].length;
return zeros + 1;
}
console.log(_d(0.1));
console.log(_d(0.01));
console.log(_d(0.000000001));
Do you want some thing like this
function decimalPlaces(num) {
var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
if (!match) { return 0; }
return Math.max(
0,
// Number of digits right of decimal point.
(match[1] ? match[1].length : 0)
// Adjust for scientific notation.
- (match[2] ? +match[2] : 0));
}
console.log(decimalPlaces(0.000000001))
First off, I got some inspiration for this answer from here:
How to avoid scientific notation for large numbers in JavaScript?
You can convert the number to a strong and then check for str.indexOf("e"). If true, then just return the scientific notation part of the string. For example:
function _d() {
// your current function here
if (str.indexOf("e")) {
var something = str.split("-")[1];
return something;
}
}
EDIT: I was working on this before your last comment to me, so this returns a string of the number, which I thought was what you wanted.
Leaving aside the point about significant digits, which is meaningful and correct but does not solve your problem, try this. We take the number, convert to string, if that string is not scientific notation then the answer is trivial. If it is scientific notation, then split the string twice (once on "e-" and then split the zeroth array on "." Add str[1]-1 zeroes to the lead of the number and add the digits to the end.
function _d(arg) {
var str = arg.toString();
if (str.indexOf("e-")) {
var digits = str.split("e-")[0];
var zeroes = str.split("e-")[1];
var zero = Number(zeroes);
var each = digits.split(".");
var something = "0.";
for (var i = 0; i < zeroes-1; i++) {
something += "0";
}
for (var j = 0; j < each.length; j++) {
something = something + each[j];
}
return something;
}
}
This won't work with very large numbers or very small negative numbers. And its pretty convoluted.
The other way is to use .toString() and then look for .length-2(2 characters - '0.'. It should give you the number of zeros.
The advantage of this method is you don't need to know the number of maximum decimals in the number.
To keep it as the full decimal:
Number(0.000001)
// 0.000001
To show it as a string:
0.000001.toFixed(6)
// "0.000001"
This is what works as expected:
This parseFloat(newValue).toLocaleString("de-DE", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) will result in a String: "34.886,55"
This does not work as expected:
For parseFloat("34.886,55") I get a Number, but I lost everything after the comma: 34.886.
How can I fix this problem ?
I wrote this simple function for doing locale based parseFloat, to the best of my knowledge, the assumptions for this function are.
The decimal operator will come once.
So split it at the decimal("," in our case) to get two elements in an array.
Then remove the thousands separator("." in our case) for each of the elements in the array.
Then joinback the array with the decimal separator ("." in our case)
finally return the parseFloat value of this joined number!
var str = "34.886,55"
console.log("before: ", str);
str = localeParseFloat(str, ",", ".");
console.log("resulting floating value from the function: ", str);
function localeParseFloat(str, decimalChar, separatorChar){
var out = [];
str.split(",").map(function(x){
x = x.replace(".", "");
out.push(x);
})
out = out.join(".");
return parseFloat(out);
}
<script src="https://rawgit.com/eu81273/jsfiddle-console/master/console.js"></script>
Here's a brief example of the number parsing... Commas are not valid in numbers, only the full stop represents the decimal separator. If it is encountered, the string conversion stop, as you can see on the second log. Also check the third log, I've added a check for the value to be true if it's lower than 100, making it more obvious that you are not playing with thousand separator but really decimal separator.
console.log(parseFloat("34.886.55"));
console.log(parseFloat("34,886.55"));
console.log(parseFloat("34.886,55"), parseFloat("34.886,55") < 100);
I'm new to RegExp and to JS in general (Coming from Python), so this might be an easy question:
I'm trying to code an algebraic calculator in Javascript that receives an algebraic equation as a string, e.g.,
string = 'x^2 + 30x -12 = 4x^2 - 12x + 30';
The algorithm is already able to break the string in a single list, with all values on the right side multiplied by -1 so I can equate it all to 0, however, one of the steps to solve the equation involves creating a hashtable/dictionary, having the variable as key.
The string above results in a list eq:
eq = ['x^2', '+30x', '-12', '-4x^2', '+12x', '-30'];
I'm currently planning on iterating through this list, and using RegExp to identify both variables and the respective multiplier, so I can create a hashTable/Dictionary that will allow me to simplify the equation, such as this one:
hashTable = {
'x^2': [1, -4],
'x': [30, 12],
' ': [-12]
}
I plan on using some kind of for loop to iter through the array, and applying a match on each string to get the values I need, but I'm quite frankly, stumped.
I have already used RegExp to separate the string into the individual parts of the equation and to remove eventual spaces, but I can't imagine a way to separate -4 from x^2 in '-4x^2'.
You can try this
(-?\d+)x\^\d+.
When you execute match function :
var res = "-4x^2".match(/(-?\d+)x\^\d+/)
You will get res as an array : [ "-4x^2", "-4" ]
You have your '-4' in res[1].
By adding another group on the second \d+ (numeric char), you can retrieve the x power.
var res = "-4x^2".match(/(-?\d+)x\^(\d+)/) //res = [ "-4x^2", "-4", "2" ]
Hope it helps
If you know that the LHS of the hashtable is going to be at the end of the string. Lets say '4x', x is at the end or '-4x^2' where x^2 is at end, then we can get the number of the expression:
var exp = '-4x^2'
exp.split('x^2')[0] // will return -4
I hope this is what you were looking for.
function splitTerm(term) {
var regex = /([+-]?)([0-9]*)?([a-z](\^[0-9]+)?)?/
var match = regex.exec(term);
return {
constant: parseInt((match[1] || '') + (match[2] || 1)),
variable: match[3]
}
}
splitTerm('x^2'); // => {constant: 1, variable: "x^2"}
splitTerm('+30x'); // => {constant: 30, variable: "x"}
splitTerm('-12'); // => {constant: -12, variable: undefined}
Additionally, these tool may help you analyze and understand regular expressions:
https://regexper.com/
https://regex101.com/
http://rick.measham.id.au/paste/explain.pl