How can I form a regular expression that match the unique numbers that repeat in a repeating decimals?
Currently my regular expressions is the following.
var re = /(?:[^\.]+\.\d*)(\d+)+(?:\1)$/;
Example:
// Pass
deepEqual( func(1/111), [ "0.009009009009009009", "009" ] );
// Fails, since func(11/111) returns [ "0.099099099099099", "9" ]
deepEqual( func(11/111), [ "0.099099099099099", "099" ] );
Live demo here: http://jsfiddle.net/9dGsw/
Here's my code.
// Goal: Find the pattern within repeating decimals.
// Problem from: Ratio.js <https://github.com/LarryBattle/Ratio.js>
var func = function( val ){
var re = /(?:[^\.]+\.\d*)(\d+)+(?:\1)$/;
var match = re.exec( val );
if( !match ){
val = (val||"").toString().replace( /\d$/, '' );
match = re.exec( val );
}
return match;
};
test("find repeating decimals.", function() {
deepEqual( func(1), null );
deepEqual( func(1/10), null );
deepEqual( func(1/111), [ "0.009009009009009009", "009" ] );
// This test case fails...
deepEqual( func(11/111), [ "0.099099099099099", "099" ],
"What's wrong with re in func()?" );
deepEqual( func(100/111), [ "0.9009009009009009", "009"] );
deepEqual( func(1/3), [ "0.3333333333333333", "3"]);
});
Ok. I somewhat solved my own problem by taking Joel's advice.
The problem was that the regular expression section, (\d+)+(?:\1)$, was matching the pattern closest to the end of the string, which made it return "9", instead of "099" for the string "0.099099099099099".
The way I overcame this problem was by setting the match length to 2 or greater, like so.
(\d{2,})+(?:\1)$,
and filtering the result with /^(\d+)(?:\1)$/, incase that a pattern is stuck inside a pattern.
Here's the code that passes all my test cases.
Live Demo: http://jsfiddle.net/9dGsw/1/
var func = function( val ){
val = (val || "").toString();
var RE_PatternInRepeatDec = /(?:[^\.]+\.\d*)(\d{2,})+(?:\1)$/,
RE_RepeatingNums = /^(\d+)(?:\1)$/,
match = RE_PatternInRepeatDec.exec( val );
if( !match ){
// Try again but take off last digit incase of precision error.
val = val.replace( /\d$/, '' );
match = RE_PatternInRepeatDec.exec( val );
}
if( match && 1 < match.length ){
// Reset the match[1] if there is a pattern inside the matched pattern.
match[1] = RE_RepeatingNums.test(match[1]) ? RE_RepeatingNums.exec(match[1])[1] : match[1];
}
return match;
};
Thank you for everyone that helped.
Use: var re = /^(?:\d*)\.(\d{1,3})(?:\1)+$/
I have defined the min/max length with {min,max} of the repeating decimal because otherwise 009009009 would match in the first test case as well. Maybe it is still not the final solution, but at least a hint.
Related
I've encountered this weird behavior:
I'm on a breakpoint (variables don't change). At the console you can see, that each time I try to evaluate regexp methods on the same unchanging variable "text" I get these opposite responses. Is there an explanation for such thing?
The relevant code is here:
this.singleRe = /<\$([\s\S]*?)>/g;
while( this.singleRe.test( text ) ){
match = this.singleRe.exec( text );
result = "";
if( match ){
result = match[ 1 ].indexOf( "." ) != -1 ? eval( "obj." + match[ 1 ] ) : eval( "value." + match[ 1 ] );
}
text = text.replace( this.singleRe , result );
}
When you use regex with exec() and a global flag - g, a cursor is changing each time, like here:
var re = /\w/g;
var s = 'Hello regex world!'
re.exec(s); // => ['H']
re.exec(s); // => ['e']
re.exec(s); // => ['l']
re.exec(s); // => ['l']
re.exec(s); // => ['o']
Note the g flag! This means that regex will match multiple occurencies instead of one!
EDIT
I suggest instead of using regex.exec(string) to use string.match(regex) if possible. This will yield an array of occurences and it is easy to inspect the array or to iterate through it.
I'm trying to parse apart a string like this with javascript:
var mystr = "param1('stringValue'), param2(IntegerValue IntegerValue)";
Where in the end I need to get each of the param names (param1 and param2) and values (either one string value, or two integer values).
However, the original string could have any number of parameters, such as:
"param1('stringValue')"
"param1('stringValue'), param2(IntegerValue IntegerValue), param2(IntegerValue IntegerValue)"
Requirements:
Between each parameter is a comma, and values are within the parentheses.
Within the parentheses could either be one string value, or two integers with a space in between.
There could be any number of parameters (but at least 1)
var str = "param1('stringValue'), param2(1 2), param2(34 555)";
var re = /(\w+)\((?:'([^']+)'|(\d+) (\d+))\)/g;
var a = [], match, value;
while(match = re.exec(str)) {
if (match[2] != null) value = match[2];
else value = [parseInt(match[3]), parseInt(match[4])];
a.push([match[1], value]);
}
a; // [["param1", "stringValue"], ["param2", [1, 2]], ["param2", [34, 555]]]
Strings should not have ' symbols in them. If you want completely foolproof solution, you need to abandon regexs and use some parser.
Parsing escaped string would be possible with something like [^']|(<?\\(?:\\\\)*)', but regexes in js doesn't support look-behinds and AFAIR variable-length look-behinds are not supported at all.
function parse( str ) {
var rparse = /([a-zA-Z0-9$_]+)\(|'([^']*)'|(\d+ \d+)/g,
cur, r = [];
while( ( cur = rparse.exec( str ) ) ) {
if( cur[1] ) {
r.push( {name: cur[1] } );
}
else if( cur[2] ) {
r[r.length-1].values = [cur[2]];
}
else if( cur[3] ) {
r[r.length-1].values = cur[3].split(" ").map( Number );
}
}
return r;
}
http://jsfiddle.net/xmajs/
Given a string
'1.2.3.4.5'
I would like to get this output
'1.2345'
(In case there are no dots in the string, the string should be returned unchanged.)
I wrote this
function process( input ) {
var index = input.indexOf( '.' );
if ( index > -1 ) {
input = input.substr( 0, index + 1 ) +
input.slice( index ).replace( /\./g, '' );
}
return input;
}
Live demo: http://jsfiddle.net/EDTNK/1/
It works but I was hoping for a slightly more elegant solution...
There is a pretty short solution (assuming input is your string):
var output = input.split('.');
output = output.shift() + '.' + output.join('');
If input is "1.2.3.4", then output will be equal to "1.234".
See this jsfiddle for a proof. Of course you can enclose it in a function, if you find it necessary.
EDIT:
Taking into account your additional requirement (to not modify the output if there is no dot found), the solution could look like this:
var output = input.split('.');
output = output.shift() + (output.length ? '.' + output.join('') : '');
which will leave eg. "1234" (no dot found) unchanged. See this jsfiddle for updated code.
It would be a lot easier with reg exp if browsers supported look behinds.
One way with a regular expression:
function process( str ) {
return str.replace( /^([^.]*\.)(.*)$/, function ( a, b, c ) {
return b + c.replace( /\./g, '' );
});
}
You can try something like this:
str = str.replace(/\./,"#").replace(/\./g,"").replace(/#/,".");
But you have to be sure that the character # is not used in the string; or replace it accordingly.
Or this, without the above limitation:
str = str.replace(/^(.*?\.)(.*)$/, function($0, $1, $2) {
return $1 + $2.replace(/\./g,"");
});
You could also do something like this, i also don't know if this is "simpler", but it uses just indexOf, replace and substr.
var str = "7.8.9.2.3";
var strBak = str;
var firstDot = str.indexOf(".");
str = str.replace(/\./g,"");
str = str.substr(0,firstDot)+"."+str.substr(1,str.length-1);
document.write(str);
Shai.
Here is another approach:
function process(input) {
var n = 0;
return input.replace(/\./g, function() { return n++ > 0 ? '' : '.'; });
}
But one could say that this is based on side effects and therefore not really elegant.
This isn't necessarily more elegant, but it's another way to skin the cat:
var process = function (input) {
var output = input;
if (typeof input === 'string' && input !== '') {
input = input.split('.');
if (input.length > 1) {
output = [input.shift(), input.join('')].join('.');
}
}
return output;
};
Not sure what is supposed to happen if "." is the first character, I'd check for -1 in indexOf, also if you use substr once might as well use it twice.
if ( index != -1 ) {
input = input.substr( 0, index + 1 ) + input.substr(index + 1).replace( /\./g, '' );
}
var i = s.indexOf(".");
var result = s.substr(0, i+1) + s.substr(i+1).replace(/\./g, "");
Somewhat tricky. Works using the fact that indexOf returns -1 if the item is not found.
Trying to keep this as short and readable as possible, you can do the following:
JavaScript
var match = string.match(/^[^.]*\.|[^.]+/g);
string = match ? match.join('') : string;
Requires a second line of code, because if match() returns null, we'll get an exception trying to call join() on null. (Improvements welcome.)
Objective-J / Cappuccino (superset of JavaScript)
string = [string.match(/^[^.]*\.|[^.]+/g) componentsJoinedByString:''] || string;
Can do it in a single line, because its selectors (such as componentsJoinedByString:) simply return null when sent to a null value, rather than throwing an exception.
As for the regular expression, I'm matching all substrings consisting of either (a) the start of the string + any potential number of non-dot characters + a dot, or (b) any existing number of non-dot characters. When we join all matches back together, we have essentially removed any dot except the first.
var input = '14.1.2';
reversed = input.split("").reverse().join("");
reversed = reversed.replace(\.(?=.*\.), '' );
input = reversed.split("").reverse().join("");
Based on #Tadek's answer above. This function takes other locales into consideration.
For example, some locales will use a comma for the decimal separator and a period for the thousand separator (e.g. -451.161,432e-12).
First we convert anything other than 1) numbers; 2) negative sign; 3) exponent sign into a period ("-451.161.432e-12").
Next we split by period (["-451", "161", "432e-12"]) and pop out the right-most value ("432e-12"), then join with the rest ("-451161.432e-12")
(Note that I'm tossing out the thousand separators, but those could easily be added in the join step (.join(','))
var ensureDecimalSeparatorIsPeriod = function (value) {
var numericString = value.toString();
var splitByDecimal = numericString.replace(/[^\d.e-]/g, '.').split('.');
if (splitByDecimal.length < 2) {
return numericString;
}
var rightOfDecimalPlace = splitByDecimal.pop();
return splitByDecimal.join('') + '.' + rightOfDecimalPlace;
};
let str = "12.1223....1322311..";
let finStr = str.replace(/(\d*.)(.*)/, '$1') + str.replace(/(\d*.)(.*)/, '$2').replace(/\./g,'');
console.log(finStr)
const [integer, ...decimals] = '233.423.3.32.23.244.14...23'.split('.');
const result = [integer, decimals.join('')].join('.')
Same solution offered but using the spread operator.
It's a matter of opinion but I think it improves readability.
How do I use JavaScript regex to validate numbers like this?
1,000
1,000.00
1000.00
1000
I tried this (the string used should not match):
test('2342342342sdfsdfsdf');
function test(t)
{
alert(/\d{1,3}(,\d{3})*(\.\d\d)?|\.\d\d/.test(t));
}
but still gives me true.
If you want to test if the complete input string matches a pattern, then you should start your regex with ^ and end with $. Otherwise you are just testing if the input string contains a substring that matches the given pattern.
^ means "Start of the line"
$ means "End of the line"
In this case it means you have to rewrite you regex to:
/^(\d{1,3}(,\d{3})*(\.\d\d)?|\.\d\d)$/
If you would omit the extra parentheses, because otherwise the "|" would have lower precedence than the ^ and $, so input like "1,234.56abc" or "abc.12" would still be valid.
I'm not sure what you mean by validate. But this might work.
//console.log is used in firebug.
var isNumber = function( str ){
return !isNaN( str.toString().replace(/[,.]/g, '') );
}
console.log( isNumber( '1,000.00' ) === true );
console.log( isNumber( '10000' ) === true );
console.log( isNumber( '1,ooo.00' ) === false );
console.log( isNumber( 'ten' ) === false );
console.log( isNumber( '2342342342sdfsdfsdf') === false );
try this
var number = '100000,000,000.00';
var regex = /^\d{1,3}(,?\d{3})*?(.\d{2})?$/g;
alert(regex.test(number));
I am currently writing an application in JavaScript where I'm matching input to regular expressions, but I also need to find a way how to match strings to parts of the regular expressions.
For example:
var invalid = "x",
potentially = "g",
valid = "ggg",
gReg = /^ggg$/;
gReg.test(invalid); //returns false (correct)
gReg.test(valid); //returns true (correct)
Now I need to find a way to somehow determine that the value of the potentially variable doesn't exactly match the /^ggg$/ expression, BUT with more input, it potentially can!
So for example in this case, the potentially variable is g, but if two more g's are appended to it, it will match the regular expression /^ggg$/
But in the case of invalid, it can never match the /^ggg$/ expression, no matter how many characters you append to it.
So how can I determine if a string has or doesn't have potential to match a particular regular expression?
Try this:
var str = "abcdefgh";
var len = str.length;
var reg = "";
for(var i = str.length - 1; i > 0; i--)
{
//replace '(' with '(?:' to make it non capturing.
reg = '(' + str[i] + reg + ')?';
}
reg = "^" + str[0] + reg + "$";
var regex = new RegExp(reg);
How about you simply "reverse" your thinking on this, and turn the "potential" into a regex, testing in the other direction, eg
var invalid = "x",
potentially = "g",
valid = "ggg",
validReg = new RegExp("^"+valid+"$"),
invalidReg = new RegExp(invalid),
potentialReg = new RegExp(potentially);
//test actual matches
validReg.test(invalid); //returns false (correct)
validReg.test(valid); //returns true (correct)
//test potential matches
potentialReg.test(valid); //returns true
invalidReg.test(valid); //returns false
Obviously the test function below isn't going to be exactly what you want ... hopefully it will give you an idea as to how to tackle the problem.
function test(reg,string){
var r = reg.exec(string);
if(r){
if(r.pop()){
return true;
}
return "potentially";
}
return false;
}
var invalid = "x",
potentially = "a",
potentially2 = "ab",
valid = "abc",
gReg = /^a(b(c)?)?$/;
alert(test(gReg,invalid)); //returns false (correct)
alert(test(gReg,potentially)); //returns "potentially" (correct)
alert(test(gReg,potentially2)); //returns "potentially" (correct)
alert(test(gReg,valid)); //returns true (correct)
There is no general solution. If the regexp is a simple string, like the one in the example (in which case there is no point in using a regexp at all), you can use simple string comparision:
var invalid = "x",
potentially = "g",
valid = "ggg";
var gReg = "ggg";
function test(t, s) {
if (t === s) return true;
if (t.indexOf(s) === 0) return "potentially";
return false;
}
test(gReg, invalid); // false
test(gReg, potentially); // "potentially"
test(gReg, valid); // true
Otherwise you can manually construct another regexp which accepts every prefix of every string gReg accepts. You will have to use ()? a lot.
function have_potential(input, valid) {
if ( (new RegExp('^' + valid + '$')).test(input) ) return false;
if ( (new RegExp(input)).test( valid ) ) return true;
return false;
}
var valid = 'aaa|bbb';
console.log( have_potential('a',valid) )
// true
console.log( have_potential('c',valid) )
// false
console.log( have_potential('aaa',valid) )
// false
Edit:
shorten version
function have_potential(input, valid) {
return ( (new RegExp(input)).test( valid ) && !(new RegExp('^' + valid + '$')).test(input) );
}
Edit2:
indexOf would be better in first place. function requires flat string inputs and "valid" may contain a list separated by "|"
function have_potential(input, valid) {
return ( valid.indexOf(input) !== -1 && !(new RegExp('^' + valid + '$')).test(input) );
}