Regex matching of range with pre-defined labels - javascript

I have the following regex that allows any number (x), or comma separated numbers (x,x), or any range (x-x) for an input on my page:
^\d+(?:(?:\s*-\s*\d+)?|(?:\s*,\s*\d+)*)$
This works perfectly but I have a new requirement. I need to allow the user to label what they are defining and allow that if it's valid. For example, they can say: Buildings: 1000-1010 or Buildings: 1001,1002.
Buildings: must be in the beginning of the string followed a colon followed by the rest. If they don't specify I won't know what they are saying.
Currently Buildings is the only label they can define, but I know more will be coming so I would also like to be able to specify that, if possible. But hopefully I can figure it out if I figure out how to allow one:
let input = 'Buildings: 1000-1001';
/^\d+(?:(?:\s*-\s*\d+)?|(?:\s*,\s*\d+)*\s*,?)$/.test(input); // should be ok
input = 'Buildings: 1000';
/^\d+(?:(?:\s*-\s*\d+)?|(?:\s*,\s*\d+)*\s*,?)$/.test(input); // should be ok
input = 'Buildings: 1000,2002';
/^\d+(?:(?:\s*-\s*\d+)?|(?:\s*,\s*\d+)*\s*,?)$/.test(input); // should be ok
For future, say I have an array of labels:
const categories: ['Buildings', 'Cars', 'Houses'];
To loop over this list and run the test and include the variable's value in the regex. Is it possible?
Are you supposed to hardcode the text? I started trying that but I kept getting errors.
Thank you

A simpler regex should do the trick along with some format strings.
for example a simpler regex like /\d+?([,-]\d+?)*?$/ should do fine matching all three of your examples.
And for the string substitution using the new RegExp syntax will allow you to use standard format strings.
For Example:
const categories = ['Buildings', 'Cars', 'Houses'];
categories.forEach((i) => {
let input = 'Buildings: 1000-1001';
let re = new RegExp(`${i}: \\d+?([,-]\\d+?)*?$`);
console.log(re.test(input));
input = 'Buildings: 1000';
re = new RegExp(`${i}: \\d+?([,-]\\d+?)*?$`);
console.log(re.test(input));
input = 'Buildings: 1000,2002,2003,2034';
re = new RegExp(`${i}: \\d+?([,-]\\d+?)*?$`);
console.log(re.test(input));
input = 'Buildings: 1000,2002';
re = new RegExp(`${i}: \\d+?([,-]\\d+?)*?$`);
console.log(re.test(input));
});
OUTPUT : Only the first 4 print True because Only the first element of the array is in each of the inputs.
true
true
true
true
false
false
...

If the labels do not contain any special regex meta character, you might shorten the pattern and use an alternation
^(?:Buildings|Cars|Houses):\s*\d+(?:\s*[-,]\s*\d+)*$
See a regex demo.
const categories = ['Buildings', 'Cars', 'Houses'];
const regex = new RegExp(`^(?:${categories.join("|")}):\\s*\\d+(?:\\s*[-,]\\s*\\d+)*$`);
[
"Buildings: 1000-1001",
"Buildings: 1000",
"Buildings: 1000,2002",
"Test: 1000,2002"
].forEach(s => console.log(`${s} --> ${regex.test(s)}`));

Related

How can I create a Regex that only lets in comma separated numbers?

I want my user to be able to add multiple numeric values separated by commas. For example,
Allowed Strings
1,2,3
.1,2.0,3.,
Not Allowed
1,,2..3
1,2,.,3,.
I have this so far:
/(\.?)(\d+)(,?)/g
As a bonus, I would also like to have the regex that I could give to the JS match method which will give me an array of the values entered by the user.
You can use a function that will split the string by the comas and then check if every items are numbers (!isNaN) to decide to return the splitted string (an array) or something else.
const allowed_1 = "1,2,3"
const allowed_2 = ".1,2.0,3.,"
const notAllowed_1 = "1,,2..3"
const notAllowed_2 = "1,2,.,3,."
const checkNumbers = (string) => {
items = string.split(",")
return items.every((item) => !isNaN(item)) ? items : "Not allowed"
}
console.log(checkNumbers(allowed_1))
console.log(checkNumbers(allowed_2))
console.log(checkNumbers(notAllowed_1))
console.log(checkNumbers(notAllowed_2))
^(((\d*\.?\d+)|(\d+\.?\d*))(,)|((\d*\.?\d+)|(\d+\.?\d*)))+$
^((\d+\.?\d*,?)|(\d+\.?\d+,?)|(\d*\.?\d+)(,?))+$
edit: thanks to #Brooke because my 1st answer had an error, now this one should work perfectly
edit2: the 2dn one is more understandable

Regex (or not) replace all matches with dynamic variables

Let's say that I have the following string taken from user input:
The ##firstvar## went to the ##secondvar## with the ##thirdvar##.
Where the values "firstvar" "secondvar" and "thirdvar" all came from user input as well, so they aren't known before runtime.
Is there a way to replace all the matches between sets of "##" with a corresponding cached variable?
Say for example I have these values cached:
cache[firstvar] = "dog"
cache[secondvar] = "river"
cache[thirdvar] = "cat"
I want the final output string to be:
The dog went to the river with the cat.
I've tried regex replace but can't figure it out when the replacements are dynamic like this.
You can replace them by using a function as second argument in String.prototype.replace().
const cache = { firstvar: "dog", secondvar: "river", thirdvar: "cat" },
text = "The ##firstvar## went to the ##secondvar## with the ##thirdvar##.",
regex = /##(.*?)##/g;
console.log( text.replace(regex, (_match, group1) => cache[group1]) );

Matching an exact word from a string

I need a way to match a word against a string and not get false positives. Let me give an example of what I mean:
"/thing" should match the string "/a/thing"
"/thing" should match the string "/a/thing/that/is/here"
"/thing" should NOT match the string "/a/thing_foo"
Basically, it should match if the exact characters are there in the first string and the second, but not if there are run-ons in the second (such as an underscore like in thing_foo).
Right now, I'm doing this, which is not working.
let found = b.includes(a); // true
Hopefully my question is clear enough. Thanks for the help!
Boy did this turn in to a classic XY Problem.
If I had to guess, you want to know if a path contains a particular segment.
In that case, split the string on a positive lookahead for '/' and use Array.prototype.includes()
const paths = ["/a/thing", "/a/thing/that/is/here", "/a/thing_foo"]
const search = '/thing'
paths.forEach(path => {
const segments = path.split(/(?=\/)/)
console.log('segments', segments)
console.info(path, ':', segments.includes(search))
})
Using the positive lookahead expression /(?=\/)/ allows us to split the string on / whilst maintaining the / prefix in each segment.
Alternatively, if you're still super keen in using a straight regex solution, you'll want something like this
const paths = ["/a/thing", "/a/thing/that/is/here", "/a/thing_foo", "/a/thing-that/is/here"]
const search = '/thing'
const rx = new RegExp(search + '\\b') // note the escaped backslash
paths.forEach(path => {
console.info(path, ':', rx.test(path))
})
Note that this will return false positives if the search string is followed by a hyphen or tilde as those are considered to be word boundaries. You would need a more complex pattern and I think the first solution handles these cases better.
I'd recommend using regular expressions...
e.g. The following regular expression /\/thing$/ - matches anything that ends with /thing.
console.log(/\/thing$/.test('/a/thing')) // true
console.log(/\/thing$/.test('/a/thing_foo')) // false
Update: To use a variable...
var search = '/thing'
console.log(new RegExp(search + '$').test('/a/thing')) // true
console.log(new RegExp(search + '$').test('/a/thing_foo')) // false
Simply with following regex you can do it
var a = "/a/thing";
var b = "/a/thing/that/is/here";
var c = "/a/thing_foo";
var pattern = new RegExp(/(:?(thing)(([^_])|$))/);
pattern.test(a) // true
pattern.test(b) // true
pattern.test(c) // false

Regular expression for excluding specific name string for the name field?

I m trying to create a regular expression that goes with all the name but excluding name like 'test','dummy','percentage','crap'
but if i m typing 'est' ,'ummy' it is not taking.
As a group it should not accept, but currently it is excluding a single character.
I tried this :
[^#test]
please ask for any confirmation
If you have to use a regular expression, use negative lookahead from the beginning of the string:
const validate = str => /^(?!(test|dummy|percentage|crap)$)/.test(str);
console.log(validate('test'));
console.log(validate('est'));
console.log(validate('testt'));
console.log(validate('foo'));
console.log(validate('dumm'));
console.log(validate('dummy'));
If you don't, use includes:
const validate = str => !['test', 'dummy', 'percentage', 'crap'].includes(str);
[^#test] matches a single character that is not one of #, t, e, and s.
Like Cerbrus said, regexes aren’t the right tool for this.
You can use a Set if you can count on IE 11+:
const forbidden = new Set([
"test",
"dummy",
"crap"
]);
function shouldAllow(word) {
return forbidden.has(word);
}
Otherwise, you can use an array and indexOf instead:
const forbidden = [
"test",
"dummy",
"crap"
];
function shouldAllow(word) {
return forbidden.indexOf(word) == -1;
}
Instead of using regex why don't you harness the power of JavaScript's array#includes to write a one liner Arrow function (=>) like this:
excludeList = ['test','dummy','percentage','crap']
isInExcludeList = (testName) => excludeList.includes(testName)
console.log(isInExcludeList('dummy')) //true
console.log(isInExcludeList('dum')) //false
Advantage: It'll make your code easy to maintain.

Regex Fetch part of string as number

I've a string something like
<dt>Source:</dt>
<dd>
Emergence: Title; 2005, Vol. 9 Issue 30, p120-203, 12p
</dd>
Now I am a regex to fetch different values for it, i.e. : Volume, issue, date etc
so, I fetch entire text using :
var attr = jQuery("dl dt:contains('Source:') ~ dd:eq(0)").text();
And use regex to fetch different values, such as :
To fetch start page I use, following regex:
var regex = new RegExp("p\\d+(?=[-\\s]{1})");
var regexValPS = attr.match(regex);
Return value : p120, expected : 120
Similarly, to fetch Volume info, I use following, regex:
var regexVol = new RegExp("Vol.\\s\\d+");
var regexValVol = attributeVal.match(regexVol);
I get : Vol. 9 , I want : 9
Similarly I am getting issue number with "Issue" text :
var regEx = new RegExp("Issue\\s\\d+");
var regExVal = attributeVal.match(regEx);
I Should get : 30 instead : Issue 30
The problem is I can't use another regex to get the desired value, can't strip/parseInt etc, and the pattern must be able to fetch information in a single regex.
Use grouping (...) and read its match »
Demo:
var str = "Emergence: Title; 2005, Vol. 9 Issue 30, p120-203, 12p";
var re = /p(\d+)(?=[\-\s])/;
document.writeln(re.exec(str)[1]); // prints: 120
re = /Vol\.\s(\d+)/;
document.writeln(re.exec(str)[1]); // prints: 9
Test it here.
Toget the desired info using a single regex, you need to take advantage of regex grouping:
var regEx = new RegExp("Issue\\s(\\d+)");
var regExVal = attributeVal.match(regEx)[1];
If you cannot modify the regex, you can maybe parse the resulting number :
var number = "Issue 30".replace(/\D/g, '');
If I understand you correctly, you do not want to do further parsing on the string values returned by the .match() calls, but can accept a different regular expression if it returns the necessary values in one statement.
Your regex needs a capture group () to retrieve the desired numbers, and place them in an array index [] (the first index [0] will hold the entire matched string, and subsequent indices hold the () captured substrings).
Instead of new RegExp() you can use the simpler /pattern/ regex literal in this case, and it is possible to extract the desired value in a single statement for all cases.
var yourString = '<dt>Source:</dt>\
<dd>\
Emergence: Title; 2005, Vol. 9 Issue 30, p120-203, 12p\
</dd>';
// Match the page, captured in index [1]
yourString.match(/p(\d+)(?=[-\s]{1})/)[1];
// "120"
// Match the Vol captured in index [1]
yourString.match(/Vol\.\s(\d+)/)[1];
// "9"
// Match the issue captured in index [1]
yourString.match(/Issue\s(\d+)/)[1];
// "30"
Here it is on jsfiddle
Try this:
var attr = jQuery("dt:contains('Source:') ~ dd:eq(0)").text();
console.log(attr);
console.log(attr.match(/p(\d+)(?=[-\s]{1})/)[1]);
console.log(attr.match(/Vol\.\s(\d+)/)[1]);
console.log(attr.match(/Issue\s(\d+)/)[1]);
For more details: JQUERY REGEX EXAMPLES TO USE WITH .MATCH().

Categories