Test fails when I include an array reference in regex (array with index in regex) JavaScript - javascript

I am doing a challenge on freeCodeCamp. I am passed an array with 2 strings, the instructions are to test to see if the letters in the second string are in the first string.
Here's what I have:
return /[arr\[1\]]/gi.test(arr[0]);
This passes all the tests except where it tries to match with a capital letter.
mutation(["hello", "Hello"]) should return true.
It's the only test that fails, I have tested my regex on regexr.com with:
/[Hello]/gi and it matches with 'hello'.
Yes, there are other ways to do it, but why does it fail when I pass the string into the regex from the array?
EDIT: https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-algorithm-scripting/mutations

keep in mind that with this: return /[arr\[1\]]/gi.test(arr[0]) you are evaluating exactly this string "arr[1]". test() is a method of RegExp, then to add variables in a regex, or build the regex as string, you should use the RegExp constructor. Like the example below.
See this for browser compatibility of flags.
function mutation(str){
var r = new RegExp(str[0].toLowerCase(), "gi")
return r.test(str[1].toLowerCase());
}
console.log(mutation(["hello", "Hello"]))

The fact that your code passes the test for ["Mary", "Army"] shows that the problem is not one of case sensitivity. The only reason your code passes any of the tests is that /[arr\[1\]]/ looks for matches against the set of characters ar1[] which coincidentally happens to correctly match 8 of the 9 tests. Anyway the other - perhaps biggest - issue is that you are not testing all of the characters in arr[1] against arr[0]; if you run #Emeeus's answer it returns false positives for many of the tests. So, to test all of the characters in arr[1] against arr[0] you need something like this:
function mutation(arr) {
return arr[1].split('').reduce((t, c) => t && new RegExp(c, 'i').test(arr[0]), true);
}
let tests = [
['hello', 'hey'],
["hello", "Hello"],
["zyxwvutsrqponmlkjihgfedcba", "qrstu"],
["Mary", "Army"],
["Mary", "Aarmy"],
["Alien", "line"],
["floor", "for"],
["hello", "neo"],
["voodoo", "no"]
];
tests.map(arr => console.log(arr[0] + ", " + arr[1] + " => " + (mutation(arr) ? 'match' : 'no match')));

JavaScript has a special syntax for Regular Expressions. Those two lines are essentially the same:
return /[arr\[1\]]/gi.test(arr[0]);
return new RegExp('[arr\\[1\\]]', 'gi').test(arr[0]);
but what you probably want is this:
new RegExp('['+arr[1]+']', 'gi').test(arr[0]);
However, you should be careful since this approach does not work if it contains special characters such as '[' or ']'.

Whenever you have a javascript variable in a regular expression, you should construct a new RegExp object. Taken from your question, it should look like this
return new RegExp(arr[1], "gi").test(arr[0]);
As one hint on freeCodeCamp.org says, you can solve the problem easier if you transform the strings into arrays, using the spread operator. No need for regular expressions.

Instead of:
return /[arr\[1\]]/gi.test(arr[0]);
you can do:
return new RegEx(arr[1], gi);
Your code uses a character match ([ ]), not a string match, so it will match anything, that has those characters directly (That's why uppercase and lowercase differs, although you have specified 'i').
The new expression directly uses the string to match, not just the characters.

Related

Regex that only matches when 4 specific words were found

As the title says, I need a Regex to check if a string has this four words (Update, Rollback, Skip, Not Now) and only return them if all of them are present, if not it doesn’t return anything.
Here is an example:
{"Update":"iVBORw0KGgo","Rollback":"iVBORw0KGgo","Skip":"iVBORw0KGgo","Not Now":"iVBORw0KGgo"}
In this case, it should return [Update, Rollback, Skip, Not Now]
{"Update":"iVBORw0KGgo","Skip":"iVBORw0KGgo","Not Now":"iVBORw0KGgo"}
In this case, it shouldn’t return any value
I tried to create one by myself but my knowledge of Regex is very basic:
(Update|Rollback|Skip|Not Now)
Thanks in advance!
EDIT
I noticed that Regex might not be the best way to achieve this.
Use this:
^(?=.*"Update"\s*:)(?=.*"Rollback"\s*:)(?=.*"Skip"\s*:)(?=.*"Not Now"\s*:)
(?=...) means that if you lookahead, you find this pattern.
So an empty match will return as soon as we find all these 4 patterns.
demo
As an alternative to regex, you can use JSON.parse to parse the string into an object and Object.keys to get the properties:
const str = `{"Update":"iVBORw0KGgo","Rollback":"iVBORw0KGgo","Skip":"iVBORw0KGgo","Not Now":"iVBORw0KGgo"}`;
const keys = Object.keys(JSON.parse(str))
const result = keys.sort().toString() == "Not Now,Rollback,Skip,Update" ? keys : "";
console.log(result)
While regex is clearly not a good tool for the job,
you can do something like this:
re.match("(?=.*Update.*)(?=.*Skip.*)",string)
"(?=WORD)" matches if the expression follows, but doesn't consume any of the string.
Of course, complete the regex by all 4 words that you want similarly.
Also notice that regex by itself doesn't return anything. You need to code for this.

filter an array based on regex expression [duplicate]

I'm doing a small javascript method, which receive a list of point, and I've to read those points to create a Polygon in a google map.
I receive those point on the form:
(lat, long), (lat, long),(lat, long)
So I've done the following regex:
\(\s*([0-9.-]+)\s*,\s([0-9.-]+)\s*\)
I've tested it with RegexPal and the exact data I receive:
(25.774252, -80.190262),(18.466465, -66.118292),(32.321384, -64.75737),(25.774252, -80.190262)
and it works, so why when I've this code in my javascript, I receive null in the result?
var polygons="(25.774252, -80.190262),(18.466465, -66.118292),(32.321384, -64.75737),(25.774252, -80.190262)";
var reg = new RegExp("/\(\s*([0-9.-]+)\s*,\s([0-9.-]+)\s*\)/g");
var result = polygons.match(reg);
I've no javascript error when executing(with debug mode of google chrome). This code is hosted in a javascript function which is in a included JS file. This method is called in the OnLoad method.
I've searched a lot, but I can't find why this isn't working. Thank you very much!
Use a regex literal [MDN]:
var reg = /\(\s*([0-9.-]+)\s*,\s([0-9.-]+)\s*\)/g;
You are making two errors when you use RegExp [MDN]:
The "delimiters" / are should not be part of the expression
If you define an expression as string, you have to escape the backslash, because it is the escape character in strings
Furthermore, modifiers are passed as second argument to the function.
So if you wanted to use RegExp (which you don't have to in this case), the equivalent would be:
var reg = new RegExp("\\(\\s*([0-9.-]+)\\s*,\\s([0-9.-]+)\\s*\\)", "g");
(and I think now you see why regex literals are more convenient)
I always find it helpful to copy and past a RegExp expression in the console and see its output. Taking your original expression, we get:
/(s*([0-9.-]+)s*,s([0-9.-]+)s*)/g
which means that the expressions tries to match /, s and g literally and the parens () are still treated as special characters.
Update: .match() returns an array:
["(25.774252, -80.190262)", "(18.466465, -66.118292)", ... ]
which does not seem to be very useful.
You have to use .exec() [MDN] to extract the numbers:
["(25.774252, -80.190262)", "25.774252", "-80.190262"]
This has to be called repeatedly until the whole strings was processed.
Example:
var reg = /\(\s*([0-9.-]+)\s*,\s([0-9.-]+)\s*\)/g;
var result, points = [];
while((result = reg.exec(polygons)) !== null) {
points.push([+result[1], +result[2]]);
}
This creates an array of arrays and the unary plus (+) will convert the strings into numbers:
[
[25.774252, -80.190262],
[18.466465, -66.118292],
...
]
Of course if you want the values as strings and not as numbers, you can just omit the +.

Replace all occurences of \n\r from a string [duplicate]

I'm doing a small javascript method, which receive a list of point, and I've to read those points to create a Polygon in a google map.
I receive those point on the form:
(lat, long), (lat, long),(lat, long)
So I've done the following regex:
\(\s*([0-9.-]+)\s*,\s([0-9.-]+)\s*\)
I've tested it with RegexPal and the exact data I receive:
(25.774252, -80.190262),(18.466465, -66.118292),(32.321384, -64.75737),(25.774252, -80.190262)
and it works, so why when I've this code in my javascript, I receive null in the result?
var polygons="(25.774252, -80.190262),(18.466465, -66.118292),(32.321384, -64.75737),(25.774252, -80.190262)";
var reg = new RegExp("/\(\s*([0-9.-]+)\s*,\s([0-9.-]+)\s*\)/g");
var result = polygons.match(reg);
I've no javascript error when executing(with debug mode of google chrome). This code is hosted in a javascript function which is in a included JS file. This method is called in the OnLoad method.
I've searched a lot, but I can't find why this isn't working. Thank you very much!
Use a regex literal [MDN]:
var reg = /\(\s*([0-9.-]+)\s*,\s([0-9.-]+)\s*\)/g;
You are making two errors when you use RegExp [MDN]:
The "delimiters" / are should not be part of the expression
If you define an expression as string, you have to escape the backslash, because it is the escape character in strings
Furthermore, modifiers are passed as second argument to the function.
So if you wanted to use RegExp (which you don't have to in this case), the equivalent would be:
var reg = new RegExp("\\(\\s*([0-9.-]+)\\s*,\\s([0-9.-]+)\\s*\\)", "g");
(and I think now you see why regex literals are more convenient)
I always find it helpful to copy and past a RegExp expression in the console and see its output. Taking your original expression, we get:
/(s*([0-9.-]+)s*,s([0-9.-]+)s*)/g
which means that the expressions tries to match /, s and g literally and the parens () are still treated as special characters.
Update: .match() returns an array:
["(25.774252, -80.190262)", "(18.466465, -66.118292)", ... ]
which does not seem to be very useful.
You have to use .exec() [MDN] to extract the numbers:
["(25.774252, -80.190262)", "25.774252", "-80.190262"]
This has to be called repeatedly until the whole strings was processed.
Example:
var reg = /\(\s*([0-9.-]+)\s*,\s([0-9.-]+)\s*\)/g;
var result, points = [];
while((result = reg.exec(polygons)) !== null) {
points.push([+result[1], +result[2]]);
}
This creates an array of arrays and the unary plus (+) will convert the strings into numbers:
[
[25.774252, -80.190262],
[18.466465, -66.118292],
...
]
Of course if you want the values as strings and not as numbers, you can just omit the +.

Hubot not responding to regex variable

I've been playing around with Hubot for a bit, far too long actually but I'm getting stuck at matching a string in an array. replies is an array with some replies.
So this works:
module.exports = (robot) ->
robot.hear /seb/i, (msg) ->
msg.send msg.random replies
However, this does not:
regex = new RegExp triggers.join(), 'gi'
module.exports = (robot) ->
robot.hear regex, (msg) ->
msg.send msg.random replies
regex is a string made up of the contents of an array of strings. In this case printing out regex returns "/seb/i".
Anyone care to explain? I never was a big fan of JavaScript and Coffeescript :)
Array.prototype.join takes a string parameter as separator. It defaults to , when no parameter is given.
Let's assume we have the following array of keywords:
const triggers = ["kw1", "kw2", "kw3"]
Calling triggers.join() will return the string kw1,kw2,kw3. Passing that to RegExp constructor will create a RegExp object that matches the exact string kw1,kw2,kw3 (also ignores case because of the i flag).
In order to match multiple keywords with a regex, you must separate them with special-or (|) operator.
So instead of
regex = new RegExp triggers.join(), 'gi'
You should write
regex = new RegExp triggers.join('|'), 'i'
You might also want to drop the g (global search) flag as well, since it's enough to match the first occurrence of a keyword in your case.

Need a regex that finds "string" but not "[string]"

I'm trying to build a regular expression that parses a string and skips things in brackets.
Something like
string = "A bc defg hi [hi] jkl mnop.";
The .match() should return "hi" but not [hi]. I've spent 5 hours running through RE's but I'm throwing in the towel.
Also this is for javascript or jquery if that matters.
Any help is appreciated. Also I'm working on getting my questions formatted correctly : )
EDIT:
Ok I just had a eureka moment and figured out that the original RegExp I was using actually did work. But when I was replaces the matches with the [matches] it simply replaced the first match in the string... over and over. I thought this was my regex refusing to skip the brackets but after much time of trying almost all of the solutions below, I realized that I was derping Hardcore.
When .replace was working its magic it was on the first match, so I quite simply added a space to the end of the result word as follows:
var result = string.match(regex);
var modifiedResult = '[' + result[0].toString() + ']';
string.replace(result[0].toString() + ' ', modifiedResult + ' ');
This got it to stop targeting the original word in the string and stop adding a new set of brackets to it with every match. Thank you all for your help. I am going to give answer credit to the post that prodded me in the right direction.
preprocess the target string by removing everything between brackets before trying to match your RE
string = "A bc defg hi [hi] jkl mnop."
tmpstring = string.replace(/\[.*\]/, "")
then apply your RE to tmpstring
correction: made the match for brackets eager per nhahtd comment below, and also, made the RE global
string = "A bc defg hi [hi] jkl mnop."
tmpstring = string.replace(/\[.*?\]/g, "")
You don't necessarily need regex for this. Simply use string manipulation:
var arr = string.split("[");
var final = arr[0] + arr[1].split("]")[1];
If there are multiple bracketed expressions, use a loop:
while (string.indexOf("[") != -1){
var arr = string.split("[");
string = arr[0] + arr.slice(1).join("[").split("]").slice(1).join("]");
}
Using only Regular Expressions, you can use:
hi(?!])
as an example.
Look here about negative lookahead: http://www.regular-expressions.info/lookaround.html
Unfortunately, javascript does not support negative lookbehind.
I used http://regexpal.com/ to test, abcd[hi]jkhilmnop as test data, hi(?!]) as the regex to find. It matched 'hi' without matching '[hi]'. Basically it matched the 'hi' so long as there was not a following ']' character.
This of course, can be expanded if needed. This has a benefit of not requiring any pre-processing for the string.
r"\[(.*)\]"
Just play arounds with this if you wanto to use regular expressions.
What do yo uwant to do with it? If you want to selectively replace parts like "hi" except when it's "[hi]", then I often use a system where I match what I want to avoid first and then what I want to watch; if it matches what I want to avoid then I return the match, otherwise I return the processed match.
Like this:
return string.replace(/(\[\w+\])|(\w+)/g, function(all, m1, m2) {return m1 || m2.toUpperCase()});
which, with the given string, returns:
"A BC DEFG HI [hi] JKL MNOP."
Thus: it replaces every word with uppercase (m1 is empty), except if the word is between square brackets (m1 is not empty).
This builds an array of all the strings contained in [ ]:
var regex = /\[([^\]]*)\]/;
var string = "A bc defg hi [hi] [jkl] mnop.";
var results=[], result;
while(result = regex.exec(string))
results.push(result[1]);
edit
To answer to the question, this regex returns the string less all is in [ ], and trim whitespaces:
"A bc defg [hi] mnop [jkl].".replace(/(\s{0,1})\[[^\]]*\](\s{0,1})/g,'$1')
Instead of skipping the match you can probably try something different - match everything but do not capture the string within square brackets (inclusive) with something like this:
var r = /(?:\[.*?[^\[\]]\])|(.)/g;
var result;
var str = [];
while((result = r.exec(s)) !== null){
if(result[1] !== undefined){ //true if [string] matched but not captured
str.push(result[1]);
}
}
console.log(str.join(''));
The last line will print parts of the string which do not match the [string] pattern. For example, when called with the input "A [bc] [defg] hi [hi] j[kl]u m[no]p." the code prints "A hi ju mp." with whitespaces intact.
You can try different things with this code e.g. replacing etc.

Categories

Resources