Related
What I'm trying to accomplish:
I'm trying to create a regex program that can do the following:
A^C + B^C = (A + B)^C
My regex:
/(^|[^^*/\dt.-])(-?)(\d+\.?(?:\d+)?)x(?:(?:\^(\d+\.?(?:\d+)?))|(?:\^\(([\s\S]+)\)))?([+-])(-?)(\d+\.?(?:\d+)?)x(?:(?:\^(\d+\.?(?:\d+)?))|(?:\^\(([\s\S]+)\)))?(?=$|[^(^*/!\d\.])/
Broken down into chunks:
/(^|[^^*/\dt.-]) characters that shouldn't precede the match
(-?)(\d+\.?(?:\d+)?)x a (negative) number followed by an x
(?:(?:\^(\d+\.?(?:\d+)?))|(?:\^\(([\s\S]+)\)))? possibly x raised to a number or something between parentheses
([+-]) plus or minus
(-?)(\d+\.?(?:\d+)?)x a (negative) number followed by an x
(?:(?:\^(\d+\.?(?:\d+)?))|(?:\^\(([\s\S]+)\)))? Same power thing
(?=$|[^(^*/!\d\.])/ characters that shouldn't follow the match
Sample text:
What it does is it finds matches like the fourth example here, where the powers, between the parentheses aren't the same. I then check in the function I provide in the replace regex method that the powers are the same, and the equation can be simplified.
5-3x+7x //Here it finds 3x+7x
3x^2+4x^2-5 //Here it finds 3x^2+4x^2
3x^2+5x^3+8x^3 //Here it finds 3x^2 + 5x^3, but not 5x^3 + 8x^3 <-- The problem
3x^(5x+7x)+5x^(6x+6x) //Here it finds 3x^(5x+7x)+5x^(6x+6x) but not the inner 5x+7x and 6x+6x <--Also the problem
What I've tried:
I tried implementing this solution, but I don't know how to use that with the regex replace function: stackoverflow: How can I match overlapping strings with regex?
My code:
Expected result: 3x^2+13x^3
Actual result: 3x^2+5x^3+8x^3 (same as input)
var solvedEquation = "3x^2+5x^3+8x^3";
addXterms();
console.log(solvedEquation);
function addXterms() {
var legitPattern = true;
var addVariablesPattern = /(^|[^^*/\dt.-])(-?)(\d+\.?(?:\d+)?)x(?:(?:\^(\d+\.?(?:\d+)?))|(?:\^\(([\s\S]+)\)))?([+-])(-?)(\d+\.?(?:\d+)?)x(?:(?:\^(\d+\.?(?:\d+)?))|(?:\^\(([\s\S]+)\)))?(?=$|[^(^*/!\d\.])/g;
while (addVariablesPattern.test(solvedEquation)) {
solvedEquation = solvedEquation.replace(addVariablesPattern, function (match, operator1, minusSign1, num1, numPower1, parPower1, operator2, minusSign2, num2, numPower2, parPower2) {
var result;
if (numPower1 == numPower2 && parPower1 == parPower2) {
num1 = parseFloat(num1) * (minusSign1 == "-" ? -1 : 1);
num2 = parseFloat(num2) * (minusSign2 == "-" ? -1 : 1);
if (operator2 == "+") {
result = num1 + num2;
} else {
result = num1 - num2;
}
equationSolved = false;
if (numPower1 != "" && typeof numPower1 != "undefined") {
return operator1 + result + "x^" + numPower1;
} else if (parPower1 != "" && typeof parPower1 != "undefined") {
return operator1 + result + "x^(" + parPower1 + ")";
} else {
return operator1 + result + "x";
}
} else {
legitPattern = false;
return match;
}
});
}
}
My problem is to split a string which contains a logical operation.
For example, here is my sample string:
var rule = "device2.temperature > 20 || device2.humidity>68 && device3.temperature >10"
I need to parse that string in a way that I can easily operate my logic and I am not sure which approach would be better.
PS: Please keep in mind that those rule strings can have 10 or more different condition combinations, like 4 ANDs and 6 ORs.
Assuming no parentheses, I might go with something like this (JavaScript code):
function f(v,op,w){
var ops = {
'>': function(a,b){ return a > b; },
'<': function(a,b){ return a < b; },
'||': function(a,b){ return a || b; },
'&&': function(a,b){ return a && b; },
'==': function(a,b){ return a == b;}
}
if (ops[op]){
return ops[op](v,w);
} else alert('Could not recognize the operator, "' + op + '".');
}
Now if you can manage to get a list of expressions, you can evaluate them in series:
var exps = [[6,'>',7],'||',[12,'<',22], '&&', [5,'==',5]];
var i = 0,
result = typeof exps[i] == 'object' ? f(exps[i][0],exps[i][1],exps[i][2]) : exps[i];
i++;
while (exps[i] !== undefined){
var op = exps[i++],
b = typeof exps[i] == 'object' ? f(exps[i][0],exps[i][1],exps[i][2]) : exps[i];
result = f(result,op,b);
i++;
}
console.log(result);
If you are absolutely sure that the input is always going to be valid JavaScript
var rule = "device2.temperature > 20 || device2.humidity>68 && device3.temperature >10"
var rulePassed = eval(rule);
Keep in mind that in most cases "eval" is "evil" and has the potential to introduce more problems than it solves.
function parse(rule){
return Function("ctx", "return("+rule.replace(/[a-z$_][a-z0-9$_\.]*/gi, "ctx.$&")+")");
}
a little bit better than eval, since it will most likely throw errors, when sbd. tries to inject some code.
Because it will try to access these properties on the ctx-object instead of the window-object.
var rule = parse("device2.temperature > 20 || device2.humidity>68 && device3.temperature >10");
var data = {
device2: {
temperature: 18,
humidity: 70
},
device3: {
temperature: 15,
humidity: 75
}
};
console.log( rule.toString() );
console.log( rule(data) );
Overkill:
beware, not fully tested. may still contain errors
And, code doesn't check wether syntax is valid, only throws on a few obvious errors.
var parse = (function(){
function parse(){
var cache = {};
//this may be as evil as eval, so take care how you use it.
function raw(v){ return cache[v] || (cache[v] = Function("return " + v)) }
//parses Strings and converts them to operator-tokens or functions
function parseStrings(v, prop, symbol, number, string){
if(!prop && !symbol && !number && !string){
throw new Error("unexpected/unhandled symbol", v);
}else{
var w;
switch(prop){
//keywords
case "true":
case "false":
case "null":
w = raw( v );
break;
}
tokens.push(
w ||
~unary.indexOf(prop) && v ||
prop && parse.fetch(v) ||
number && raw( number ) ||
string && raw( string ) ||
symbol
);
}
}
var tokens = [];
for(var i = 0; i < arguments.length; ++i){
var arg = arguments[i];
switch(typeof arg){
case "number":
case "boolean":
tokens.push(raw( arg ));
break;
case "function":
tokens.push( arg );
break;
case "string":
//abusing str.replace() as kind of a RegEx.forEach()
arg.replace(matchTokens, parseStrings);
break;
}
}
for(var i = tokens.lastIndexOf("("), j; i>=0; i = tokens.lastIndexOf("(")){
j = tokens.indexOf(")", i);
if(j > 0){
tokens.splice(i, j+1-i, process( tokens.slice( i+1, j ) ));
}else{
throw new Error("mismatching parantheses")
}
}
if(tokens.indexOf(")") >= 0) throw new Error("mismatching parantheses");
return process(tokens);
}
//combines tokens and functions until a single function is left
function process(tokens){
//unary operators like
unary.forEach(o => {
var i = -1;
while((i = tokens.indexOf(o, i+1)) >= 0){
if((o === "+" || o === "-") && typeof tokens[i-1] === "function") continue;
tokens.splice( i, 2, parse[ unaryMapping[o] || o ]( tokens[i+1] ));
}
})
//binary operators
binary.forEach(o => {
for(var i = tokens.lastIndexOf(o); i >= 0; i = tokens.lastIndexOf(o)){
tokens.splice( i-1, 3, parse[ o ]( tokens[i-1], tokens[i+1] ));
}
})
//ternary operator
for(var i = tokens.lastIndexOf("?"), j; i >= 0; i = tokens.lastIndexOf("?")){
if(tokens[i+2] === ":"){
tokens.splice(i-1, 5, parse.ternary(tokens[i-1], tokens[i+1], tokens[i+3] ));
}else{
throw new Error("unexpected symbol")
}
}
if(tokens.length !== 1){
throw new Error("unparsed tokens left");
}
return tokens[0];
}
var unary = "!,~,+,-,typeof".split(",");
var unaryMapping = { //to avoid collisions with the binary operators
"+": "plus",
"-": "minus"
}
var binary = "**,*,/,%,+,-,<<,>>,>>>,<,<=,>,>=,==,!=,===,!==,&,^,|,&&,||".split(",");
var matchTokens = /([a-z$_][\.a-z0-9$_]*)|([+\-*/!~^]=*|[\(\)?:]|[<>&|=]+)|(\d+(?:\.\d*)?|\.\d+)|(["](?:\\[\s\S]|[^"])+["]|['](?:\\[\s\S]|[^'])+['])|\S/gi;
(function(){
var def = { value: null };
var odp = (k,v) => { def.value = v; Object.defineProperty(parse, k, def) };
unary.forEach(o => {
var k = unaryMapping[o] || o;
k in parse || odp(k, Function("a", "return function(ctx){ return " + o + "(a(ctx)) }"));
})
//most browsers don't support this syntax yet, so I implement this manually
odp("**", (a,b) => (ctx) => Math.pow(a(ctx), b(ctx)));
binary.forEach(o => {
o in parse || odp(o, Function("a,b", "return function(ctx){ return a(ctx) "+o+" b(ctx) }"));
});
odp("ternary", (c,t,e) => ctx => c(ctx)? t(ctx): e(ctx));
odp("fetch", key => {
var a = key.split(".");
return ctx => {
//fetches a path, like devices.2.temperature
//does ctx["devices"][2]["temperature"];
for(var i=0, v = ctx /*|| window*/; i<a.length; ++i){
if(v == null) return void 0;
v = v[a[i]];
}
return v;
}
});
/* some sugar */
var aliases = {
"or": "||",
"and": "&&",
"not": "!"
}
for(var name in aliases) odp(name, parse[aliases[name]]);
})();
return parse;
})();
and your code:
var data = {
device2: {
temperature: 18,
humidity: 70
},
device3: {
temperature: 15,
humidity: 75
}
};
//you get back a function, that expects the context to work on (optional).
//aka. (in wich context/object is `device2` defined?)
var rule = parse("device2.temperature > 20 || device2.humidity>68 && device3.temperature >10");
console.log("your rule resolved:", rule(data));
sugar:
var rule1 = parse("device2.temperature > 20");
var rule2 = parse("device2.humidity>68 && device3.temperature >10");
//partials/combining rules to new ones
//only `and` (a && b), `or` (a || b), `plus` (+value), `minus` (-value) and 'not', (!value) have named aliases
var rule3 = parse.or(rule1, rule2);
//but you can access all operators like this
var rule3 = parse['||'](rule1, rule2);
//or you can combine functions and strings
var rule3 = parse(rule1, "||", rule2);
console.log( "(", rule1(data), "||", rule2(data), ") =", rule3(data) );
//ternary operator and Strings (' and " supported)
var example = parse(rule1, "? 'device2: ' + device2.temperature : 'device3: ' + device3.temperature");
console.log( example(data) )
What else to know:
Code handles operator precedence and supports round brackets
If a Path can't be fetched, it the particular function returns undefined (no Errors thrown here)
Access to Array-keys in the paths: parse("devices.2.temperature") fetches devices[2].temperature
not implemented:
parsing Arrays and parsing function-calls and everything around value modification. This engine does some computation, it expects some Value in, and gives you a value out. No more, no less.
I have a string that contains Boolean logic something like:
var test = "(true)&&(false)&&!(true||true)"
What is a good way to evaluate this string in JavaScript to get the boolean value of false in this case
I know we could use eval() or new Function().. - but is that a safe approach?
I am guessing the other option would be to write a custom parser. Being a fairly new person to JS, would that be a lot of effort? I could not find any examples of parsers for Boolean logic expressions
Any other alternatives?
As long as you can guarantee it to be safe, I think you could use eval.
Maybe by treating it before doing an eval?
var test = "(true)&&(false)&&!(true||true)"
var safe = test.replace(/true/ig, "1").replace(/false/ig, "0");
var match = safe.match(/[0-9&!|()]*/ig);
if(match) {
var result = !!eval(match[0]);
}
Javascript has a ternary operator you could use:
var i = result ? 1 : 0;
Here, result is Boolean value either True or False.
So, Your question will be something like that after this operation.
(1)&(0)&!(1||1)
I hope you can better evaluate now this Boolean logic.
you can use eval,
Eg: eval("(true)&&(false)&&!(true||true)");
Try this code
function processExpression(expr)
{
while (expr.indexOf("(" ) != -1 )
{
expr = expr.replace(/\([\w|]+\)/g, function(matched){ return processBrace(matched)});
}
return expr = processBrace( "(" + expr + ")" );
}
function processBrace(str)
{
return str.substring(1).slice(0,-1).split(/(?=&|\|)/).map(function(value,index,arr){
if ( index != 0 && index%2 == 0 ) { return arr[index-1] + value } else if(index==0){return value;} else {return ""}
}).filter(function(val){return val.length > 0}).reduce(function(prev,current){
var first = Boolean(prev);
var operator = current.substring(0,2);
var operand = current.substring(2);
while ( operand.indexOf("!") != -1 )
{
var boolval = operand.match(/\w+/)[0] == "false"; //flip the value by comparing it with false
var negations = operand.match(/\W+/)[0];
operand = negations.substring(1) + boolval;
}
var second = operand == "true";
var output = operator == "&&" ? (first && second) : (first || second);
return output;
});
}
DEMO
function processExpression(expr)
{
while (expr.indexOf("(" ) != -1 )
{
expr = expr.replace(/\([\w|]+\)/g, function(matched){ return processBrace(matched)});
}
return expr = processBrace( "(" + expr + ")" );
}
function processBrace(str)
{
return str.substring(1).slice(0,-1).split(/(?=&|\|)/).map(function(value,index,arr){
if ( index != 0 && index%2 == 0 ) { return arr[index-1] + value } else if(index==0){return value;} else {return ""}
}).filter(function(val){return val.length > 0}).reduce(function(prev,current){
var first = Boolean(prev);
var operator = current.substring(0,2);
var operand = current.substring(2);
while ( operand.indexOf("!") != -1 )
{
var boolval = operand.match(/\w+/)[0] == "false"; //flip the value by comparing it with false
var negations = operand.match(/\W+/)[0];
operand = negations.substring(1) + boolval;
}
var second = operand == "true";
var output = operator == "&&" ? (first && second) : (first || second);
return output;
});
}
var example1 = "(true)&&(false)&&!(true||true)";
document.body.innerHTML += example1 + " -- " + processExpression(example1);
Try using "".match() in ternary operator condition
"(true)&&(true)&&!(true||true)".match(/false/ig)?false:true
In Javascript function:
if (Tim2Val > Tim3Val && Tim2Val < Tim4Val)
return true;
else
return false;
IF I have Variables like
Tim1Val= 8:00;
Tim2Val= 23:00;
Tim3Val= 01:00;
Tim4Val= 05:00
It is returning true. (It should return false. Can you please tell me how we can solve this?)
IF I have Variables like
Tim1Val= 8:00;
Tim2Val= 23:00;
Tim3Val= 02:00;
Tim4Val= 05:00;
It is returning false.
function fn_ConvTo24Format(MsTimeVal)
{
if(MsTimeVal=='')
{
return -1;
}
var A = MsTimeVal.split(/\D+/);
var locAMPos = MsTimeVal.indexOf('AM');
var locPMPos = MsTimeVal.indexOf('PM');
if(locAMPos ==-1 && locPMPos ==-1)
{
return MsTimeVal;
}
if(locAMPos!= -1 && A[0] + '.' + A[1]=='12.00' )
{
return 0;
}
if(locPMPos!= -1 && A[0] + '.' + A[1]=='12.00' )
{
return 12;
}
if(locAMPos!= -1 && A[0] + '.' + A[1]=='12.00' )
{
return 0;
}
if(locAMPos!= -1)
{
return A[0] + '.' + A[1];
}
if(locPMPos!= -1)
{
return (parseFloat(A[0]) + 12) + '.' + A[1];
}
return MsTimeVal;
}
Perhaps not ever using Tim1Val is part of the problem?
I suppose these values come in as string. If you compare the two, Javascript converts them to numbers, effectively changing their value.
"8:00" becomes 8 and minutes are ignored.
Maybe convert your hours and minutes in to just minutes:
function toMinutes (value) {
var parts = value.split(":");
return Number(value[0]) * 60 + Number(value[1]);
}
var Tim1Val = "8:00";
var Tim2Val = "23:00";
var Tim3Val = "2:00";
var Tim4Val = "5:00";
if (toMinutes(Tim1Val) > toMinutes(Tim3Val) && toMinutes(Tim2Val) < toMinutes(Tim4Val))
Oh and as Peter Wilkinson says, you never use Tim1Val.
Your function returns data of several types: string and numbers.
Use one type make
...
if(locAMPos!= -1)
{
return parseFloat(A[0] + '.' + A[1]);
}
....
I think it's not good way to make time comparation.
Will be better get Date class exemplar and work with it.
I have a function to add commas to numbers:
function commafy( num ) {
num.toString().replace( /\B(?=(?:\d{3})+)$/g, "," );
}
Unfortunately, it doesn't like decimals very well. Given the following usage examples, what is the best way to extend my function?
commafy( "123" ) // "123"
commafy( "1234" ) // "1234"
// Don't add commas until 5 integer digits
commafy( "12345" ) // "12,345"
commafy( "1234567" ) // "1,234,567"
commafy( "12345.2" ) // "12,345.2"
commafy( "12345.6789" ) // "12,345.6789"
// Again, nothing until 5
commafy( ".123456" ) // ".123 456"
// Group with spaces (no leading digit)
commafy( "12345.6789012345678" ) // "12,345.678 901 234 567 8"
Presumably the easiest way is to first split on the decimal point (if there is one). Where best to go from there?
Just split into two parts with '.' and format them individually.
function commafy( num ) {
var str = num.toString().split('.');
if (str[0].length >= 5) {
str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1,');
}
if (str[1] && str[1].length >= 5) {
str[1] = str[1].replace(/(\d{3})/g, '$1 ');
}
return str.join('.');
}
Simple as that:
var theNumber = 3500;
theNumber.toLocaleString();
Here are two concise ways I think maybe useful:
Number.prototype.toLocaleString
This method can convert a number to a string with a language-sensitive representation. It allows two parameters, which is locales & options. Those parameters may be a bit confusing, for more detail see that doc from MDN above.
In a word, you could simply use is as below:
console.log(
Number(1234567890.12).toLocaleString()
)
// log -> "1,234,567,890.12"
If you see different with me that because we ignore both two parameters and it will return a string base on your operation system.
Use regex to match a string then replace to a new string.
Why we consider this? The toLocaleString() is a bit confusing and not all browser supported, also toLocaleString() will round the decimal, so we can do it in another way.
// The steps we follow are:
// 1. Converts a number(integer) to a string.
// 2. Reverses the string.
// 3. Replace the reversed string to a new string with the Regex
// 4. Reverses the new string to get what we want.
// This method is use to reverse a string.
function reverseString(str) {
return str.split("").reverse().join("");
}
/**
* #param {string | number}
*/
function groupDigital(num) {
const emptyStr = '';
const group_regex = /\d{3}/g;
// delete extra comma by regex replace.
const trimComma = str => str.replace(/^[,]+|[,]+$/g, emptyStr)
const str = num + emptyStr;
const [integer, decimal] = str.split('.')
const conversed = reverseString(integer);
const grouped = trimComma(reverseString(
conversed.replace(/\d{3}/g, match => `${match},`)
));
return !decimal ? grouped : `${grouped}.${decimal}`;
}
console.log(groupDigital(1234567890.1234)) // 1,234,567,890.1234
console.log(groupDigital(123456)) // 123,456
console.log(groupDigital("12.000000001")) // 12.000000001
Easiest way:
1
var num = 1234567890,
result = num.toLocaleString() ;// result will equal to "1 234 567 890"
2
var num = 1234567.890,
result = num.toLocaleString() + num.toString().slice(num.toString().indexOf('.')) // will equal to 1 234 567.890
3
var num = 1234567.890123,
result = Number(num.toFixed(0)).toLocaleString() + '.' + Number(num.toString().slice(num.toString().indexOf('.')+1)).toLocaleString()
//will equal to 1 234 567.890 123
4
If you want ',' instead of ' ':
var num = 1234567.890123,
result = Number(num.toFixed(0)).toLocaleString().split(/\s/).join(',') + '.' + Number(num.toString().slice(num.toString().indexOf('.')+1)).toLocaleString()
//will equal to 1,234,567.890 123
If not working, set the parameter like: "toLocaleString('ru-RU')"
parameter "en-EN", will split number by the ',' instead of ' '
All function used in my code are native JS functions. You'll find them in GOOGLE or in any JS Tutorial/Book
If you are happy with the integer part (I haven't looked at it closly), then:
function formatDecimal(n) {
n = n.split('.');
return commafy(n[0]) + '.' + n[1];
}
Of course you may want to do some testing of n first to make sure it's ok, but that's the logic of it.
Edit
Ooops! missed the bit about spaces! You can use the same regular exprssion as commafy except with spaces instead of commas, then reverse the result.
Here's a function based on vol7ron's and not using reverse:
function formatNum(n) {
var n = ('' + n).split('.');
var num = n[0];
var dec = n[1];
var r, s, t;
if (num.length > 3) {
s = num.length % 3;
if (s) {
t = num.substring(0,s);
num = t + num.substring(s).replace(/(\d{3})/g, ",$1");
} else {
num = num.substring(s).replace(/(\d{3})/g, ",$1").substring(1);
}
}
if (dec && dec.length > 3) {
dec = dec.replace(/(\d{3})/g, "$1 ");
}
return num + (dec? '.' + dec : '');
}
I have extended #RobG's answer a bit more and made a sample jsfiddle
function formatNum(n, prec, currSign) {
if(prec==null) prec=2;
var n = ('' + parseFloat(n).toFixed(prec).toString()).split('.');
var num = n[0];
var dec = n[1];
var r, s, t;
if (num.length > 3) {
s = num.length % 3;
if (s) {
t = num.substring(0,s);
num = t + num.substring(s).replace(/(\d{3})/g, ",$1");
} else {
num = num.substring(s).replace(/(\d{3})/g, ",$1").substring(1);
}
}
return (currSign == null ? "": currSign +" ") + num + (dec? '.' + dec : '');
}
alert(formatNum(123545.3434));
alert(formatNum(123545.3434,2));
alert(formatNum(123545.3434,2,'€'));
and extended same way the #Ghostoy's answer
function commafy( num, prec, currSign ) {
if(prec==null) prec=2;
var str = parseFloat(num).toFixed(prec).toString().split('.');
if (str[0].length >= 5) {
str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1,');
}
if (str[1] && str[1].length >= 5) {
str[1] = str[1].replace(/(\d{3})/g, '$1 ');
}
return (currSign == null ? "": currSign +" ") + str.join('.');
}
alert(commafy(123545.3434));
Here you go edited after reading your comments.
function commafy( arg ) {
arg += ''; // stringify
var num = arg.split('.'); // incase decimals
if (typeof num[0] !== 'undefined'){
var int = num[0]; // integer part
if (int.length > 4){
int = int.split('').reverse().join(''); // reverse
int = int.replace(/(\d{3})/g, "$1,"); // add commas
int = int.split('').reverse().join(''); // unreverse
}
}
if (typeof num[1] !== 'undefined'){
var dec = num[1]; // float part
if (dec.length > 4){
dec = dec.replace(/(\d{3})/g, "$1 "); // add spaces
}
}
return (typeof num[0] !== 'undefined'?int:'')
+ (typeof num[1] !== 'undefined'?'.'+dec:'');
}
This worked for me:
function commafy(inVal){
var arrWhole = inVal.split(".");
var arrTheNumber = arrWhole[0].split("").reverse();
var newNum = Array();
for(var i=0; i<arrTheNumber.length; i++){
newNum[newNum.length] = ((i%3===2) && (i<arrTheNumber.length-1)) ? "," + arrTheNumber[i]: arrTheNumber[i];
}
var returnNum = newNum.reverse().join("");
if(arrWhole[1]){
returnNum += "." + arrWhole[1];
}
return returnNum;
}
Assuming your usage examples are not representative of already-working code but instead desired behavior, and you are looking for help with the algorithm, I think you are already on the right track with splitting on any decimals.
Once split, apply the existing regex to the left side, a similiar regex adding the spaces instead of commas to the right, and then rejoin the the two into a single string before returning.
Unless, of course, there are other considerations or I have misunderstood your question.
This is basically the same as the solution from Ghostoy, but it fixes an issue where numbers in the thousands are not handled properly. Changed '5' to '4':
export function commafy(num) {
const str = num.toString().split('.');
if (str[0].length >= 4) {
str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1,');
}
if (str[1] && str[1].length >= 4) {
str[1] = str[1].replace(/(\d{3})/g, '$1 ');
}
return str.join('.');
}
//Code in Java
private static String formatNumber(String myNum) {
char[] str = myNum.toCharArray();
int numCommas = str.length / 3;
char[] formattedStr = new char[str.length + numCommas];
for(int i = str.length - 1, j = formattedStr.length - 1, cnt = 0; i >= 0 && j >=0 ;) {
if(cnt != 0 && cnt % 3 == 0 && j > 0) {
formattedStr[j] = ',';
j--;
}
formattedStr[j] = str[i];
i--;
j--;
cnt++;
}
return String.valueOf(formattedStr);
}
You can do it mathematically, depending on how many digits you want to separate, you can start from one digit with 10 to 100 for 2, and so on.
function splitDigits(num) {
num=Math.ceil(num);
let newNum = '';
while (num > 1000){
let remain = num % 1000;
num = Math.floor(num / 1000);
newNum = remain + ',' + newNum;
}
return num + ',' + newNum.slice(0,newNum.length-1);
}
At first you should select the input with querySelector like:
let field = document.querySelector("input");
and then
field.addEventListener("keyup", () => {
for (let i = 1 ; i <= field.value.length; i++) {
field.value = field.value.replace(",", "");
}
let counter=0;
for (let i = 1 ; i <= field.value.length; i++) {
if ( i % ((3 * (counter+1) ) + counter) ===0){
let tempVal =field.value
field.value = addStr(tempVal,field.value.length - i,",")
counter++;
console.log(field.value);
}
}
// field.value = parseInt(field.value.replace(/\D/g, ''), 10);
// var n = parseInt(e.target.value.replace(/\D/g,''),10);
// e.target.value = n.toLocaleString();
});