Best way to store JS Regex capturing groups in array? - javascript

Exactly what title asks. I'll provide some examples while explaining my question.
Test string:
var test = "#foo# #foo# bar #foo#";
Say, I want to extract all text between # (all foos but not bar).
var matches = test.match(/#(.*?)#/g);
Using .match as above, it'll store all matches but it'll simply throw away the capturing groups it seems.
var matches2 = /#(.*?)#/g.exec(test);
The .exec method apparently returns only the first result's matched string in the position 0 of the array and my only capturing group of that match in the position 1.
I've exhausted SO, Google and MDN looking for an answer to no avail.
So, my question is, is there any better way to store only the matched capturing groups than looping through it with .exec and calling array.push to store the captured groups?
My expected array for the test above should be:
[0] => (string) foo
[1] => (string) foo
[2] => (string) foo
Pure JS and jQuery answers are accepted, extra cookies if you post JSFiddle with console.log. =]

You can use .exec too like following to build an array
var arr = [],
s = "#foo# #bar# #test#",
re = /#(.*?)#/g,
item;
while (item = re.exec(s))
arr.push(item[1]);
alert(arr.join(' '));​
Working Fiddle
Found from Here
Well, it still has a loop, if you dont want a loop then I think you have to go with .replace(). In which case the code will be like
var arr = [];
var str = "#foo# #bar# #test#"
str.replace(/#(.*?)#/g, function(s, match) {
arr.push(match);
});
Check these lines from MDN DOC which explains your query about howexec updates lastIndex property I think,
If your regular expression uses the "g" flag, you can use the exec
method multiple times to find successive matches in the same string.
When you do so, the search starts at the substring of str specified by
the regular expression's lastIndex property (test will also advance
the lastIndex property).

I'm not sure if this is the answer you are looking for but you may try the following code:
var matches = [];
var test = "#foo# #foo# bar #foo#";
test.replace(/#(.*?)#/g, function (string, match) {
matches.push(match);
});
alert(JSON.stringify(matches));
Hope it helps.

data.replace(/.*?#(.*?#)/g, '$1').split(/#/)
No loops, no functions.

In case somebody arrives with a similar need to mine, I needed a matching function for a Django-style URL config handler that could pass path "arguments" to a controller. I came up with this. Naturally it wouldn't work very well if matching '$' but it wouldn't break on '$1.00'. It's a little bit more explicit than necessary. You could just return matchedGroups from the else statement and not bother with the for loop test but ;; in the middle of a loop declaration freaks people out sometimes.
var url = 'http://www.somesite.com/calendar/2014/june/6/';
var calendarMatch = /^http\:\/\/[^\/]*\/calendar\/(\d*)\/(\w*)\/(\d{1,2})\/$/;
function getMatches(str, matcher){
var matchedGroups = [];
for(var i=1,groupFail=false;groupFail===false;i++){
var group = str.replace(matcher,'$'+i);
groupFailTester = new RegExp('^\\$'+i+'$');
if(!groupFailTester.test(group) ){
matchedGroups.push(group);
}
else {
groupFail = true;
}
}
return matchedGroups;
}
console.log( getMatches(url, calendarMatch) );

Another thought, though exec is as efficient.
var s= "#foo# #foo# bar #foo#";
s= s.match(/#([^#])*#/g).join('#').replace(/^#+|#+$/g, '').split(/#+/);

Related

How to access the first two digits of a number

I want to access the first two digits of a number, and i have tried using substring, substr and slice but none of them work. It's throwing an error saying substring is not defined.
render() {
let trial123 = this.props.buildInfo["abc.version"];
var str = trial123.toString();
var strFirstThree = str.substring(0,3);
console.log(strFirstThree);
}
I have tried the above code
output of(above code)
trial123=19.0.0.1
I need only 19.0
How can i achieve this?
I would split it by dot and then take the first two elements:
const trial = "19.0.0.1"
console.log(trial.split(".").slice(0, 2).join("."))
// 19.0
You could just split and then join:
const [ first, second ] = trial123.split('.');
const result = [ first, second ].join('.');
I have added a code snippet of the work: (explanation comes after it, line by line).
function getFakePropValue(){
return Math.round(Math.random()) == 0 ? "19.0.0.1" : null;
}
let trial123 = getFakePropValue() || "";
//var str = trial123.toString();
// is the toString() really necessary? aren't you passing it along as a String already?
var strFirstThree = trial123.split('.');
//var strFirstThree = str.substring(0,3);
//I wouldn't use substring , what if the address 191.0.0.1 ?
if(strFirstThree.length >= 2)
console.log(strFirstThree.splice(0,2).join("."));
else
console.error("prop was empty");
Because you are using React, the props value was faked with the function getFakePropValue. The code inside is irrelevant, what I am doing is returning a String randomly, in case you have allowed in your React Component for the prop to be empty. This is to show how you an create minimal robust code to avoid having exceptions.
Moving on, the following is a safety net to make sure the variable trial123 always has a string value, even if it's "".
let trial123 = getFakePropValue() || "";
That means that if the function returns something like null , the boolean expression will execute the second apart, and return an empty string "" and that will be the value for trial123.
Moving on, the line where you convert to toString I have removed, I assume you are already getting the value in string format. Next.
var strFirstThree = trial123.split('.');
That creates an array where each position holds a part of the IP addrss. So 19.0.0.1 would become [19,0,0,1] that's thanks to the split by the delimiter . . Next.
if(strFirstThree.length >= 2)
console.log(strFirstThree.splice(0,2).join("."));
else
console.error("prop was empty");
This last piece of code uses the conditional if to make sure that my array has values before I try to splice it and join. The conditional is not to avoid an exception, since splice and join on empty arrays just returns an empty string. It's rather for you to be able to raise an error or something if needed. So if the array has values, I keep the first two positions with splice(0,2) and then join that array with a '.'. I recommend it more than the substr method you were going for because what if you get a number that's 191.0.0.1 then the substr would return the wrong string back, but with splice and join that would never happen.
Things to improve
I would strongly suggest using more human comprehensible variables (reflect their use in the code)
The right path for prop value checking is through Prop.Types, super easy to use, very helpful.
Happy coding!

Confirm the Ending

I was trying to write a function to check if "str" ends with the given "target". I eventually got the right answer but was wondering what I did wrong with the code I wrote below.
function confirmEnding(str, target) {
var targetArray = target.split("");
var strArray = str.split("");
var ending = [];
for (i=target.length; i>0; i--){
if (strArray[str.length-i]==targetArray[target.length-i]){
ending.push(targetArray[target.length-i]);
} else {
ending.push(0);
}
}
return ending==targetArray;
}
Basically so if the first letters of both str and target are the same, I added it to an array called "ending" and otherwise, added 0. Then once it's done, I would compare the two arrays and it would give me true if str does end with target and false otherwise. It only returned false and I want some insight into this. Could you give me some feedback on this? Thank you.
Since ending and targetArray are two different arrays, even if the contents are same, ending==targetArray; will not give true with '==' operator.
To check the equality of contents of arrays yo will have to iterate over each content and check the equality or if you are particular about '==' you an use
ending.toString() == targetArray.toString()

JavaScript not returning expected regex results [duplicate]

This question already has answers here:
Why does a RegExp with global flag give wrong results?
(7 answers)
Closed 7 years ago.
I'm abstracting my code a bit as it is eventually going into a commercial product. I'm having some trouble getting a regex test to return the proper results.
var files = [
"Jurassic%20Park%20-%20Nedry.mp4",
'Jeb%20Corliss%20Grinding%20The%20Crack.mp4'
];
var filterSearch = function(text){
var filter = new RegExp(text, 'gi');
var displayFiles = files.filter(function(file){
return filter.test( file.toLowerCase());
});
console.log(displayFiles);
}
If I run filterSearch('J') or filterSearch('N') I'd expect to get 2 results, Jurassic Park and Jeb, instead I'm just getting one. It seems to work properly for all the other characters shared between the two files, but not for J or N. Does anyone know why this isn't working properly for me?
Thanks,
Edit: I'm able to repeat this on repl.it .
Use String.prototype.search() instead of the test() function.
Example
var filterSearch = function(text){
var filter = new RegExp(text, 'gi');
var displayFiles = files.filter(function(file){
return file.search(filter) != -1 ? true : false ;
});
console.log(filter);
console.log(displayFiles);
}
filterSearch('J');
will give you an output
["Jurassic%20Park%20-%20Nedry.mp4", "Jeb%20Corliss%20Grinding%20The%20Crack.mp4"]
This is because test() called multiple times on the same global regular expression instance will advance past the previous match. ( As stated per the MDN reference )
Read more about why test() fails when invoked multiple times
Or this answer.
When calling test on a RegExp it maintains a lastIndex property of the last match (see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test for further details).
The next time you search (even in another string) the lastIndex+1 is the starting position of the search.
The easiest way to prevent this is to leave out the global flag in the regex definition (which is not needed in this case in my opinion) or just reset the lastIndex to -1 in each iteration of the searchFilter function.
When global(g) flag is used with test() or exec() the lastIndex is set to the next position of most recent match. You will either have to reset filter.lastIndex to 0 or you could actually omit the 'g' flag
Read about lastIndex

CodeMirror - Using RegEx with overlay

I can't seem to find an example of anyone using RegEx matches to create an overlay in CodeMirror. The Moustaches example matching one thing at a time seems simple enough, but in the API, it says that the RegEx match returns the array of matches and I can't figure out what to do with it in the context of the structure in the moustaches example.
I have a regular expression which finds all the elements I need to highlight: I've tested it and it works.
Should I be loading up the array outside of the token function and then matching each one? Or is there a way to work with the array?
The other issue is that I want to apply different styling depending on the (biz|cms) option in the regex - one for 'biz' and another for 'cms'. There will be others but I'm trying to keep it simple.
This is as far as I have got. The comments show my confusion.
CodeMirror.defineMode("tbs", function(config, parserConfig) {
var tbsOverlay = {
token: function(stream, state) {
tbsArray = match("^<(biz|cms).([a-zA-Z0-9.]*)(\s)?(\/)?>");
if (tbsArray != null) {
for (i = 0; i < tbsArray.length; i++) {
var result = tbsArray[i];
//Do I need to stream.match each element now to get hold of each bit of text?
//Or is there some way to identify and tag all the matches?
}
}
//Obviously this bit won't work either now - even with regex
while (stream.next() != null && !stream.match("<biz.", false)) {}
return null;
}
};
return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "text/html"), tbsOverlay);
});
It returns the array as produced by RegExp.exec or String.prototype.match (see for example https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/match), so you probably don't want to iterate through it, but rather pick out specific elements the correspond to groups in your regexp (if (result[1] == "biz") ...)
Look at implementation of Code Mirror method match() and you'll see, that it processes method parameter for two types: string and RegExp.
Your constant in
stream.match("<biz.")
is of string type.
Define it in RegExp type:
tbsArray = /<biz./g
Thus, your stream will be matched with RegExp.

JS - jQuery inarray ignoreCase() and contains()

well, I am more of a PHP person, and my JS skills are close to none when it comes to any JS other than simple design related operations , so excuse me if I am asking the obvious .
the following operations would be a breeze in PHP (and might also be in JS - but I am fighting with unfamiliar syntax here ...)
It is some sort of input validation
var ar = ["BRS201103-0783-CT-S", "MAGIC WORD", "magic", "Words", "Magic-Word"];
jQuery(document).ready(function() {
jQuery("form#searchreport").submit(function() {
if (jQuery.inArray(jQuery("input:first").val(), ar) != -1){
jQuery("#contentresults").delay(800).show("slow");
return false;
}
This question has 2 parts .
1 - how can I make it possible for the array to be case insensitive ?
E.g. - BRS201103-0783-CT-S will give the same result as brs201103-0783-ct-s AND Brs201103-0783-CT-s or MAGIC magic Magic MaGIc
basically i need something like ignoreCase() for array , but I could not find any reference to that in jQuery nor JS...
I tried toLowerCase() - but It is not working on the array (ittirating??) and also, would it resolve the mixed case ?
2 - How can I make the function to recognize only parts or
combinations of the elements ?
E.g. - if one types only "word" , I would like it to pass as "words" , and also if someone types "some word" it should pass (containing "word" )
Part 1
You can process your array to be entirely lowercase, and lowercase your input so indexOf() will work like it's performing a case insensitive search.
You can lowercase a string with toLowerCase() as you've already figured out.
To do an array, you can use...
arr = arr.map(function(elem) { return elem.toLowerCase(); });
Part 2
You could check for a substring, for example...
// Assuming you've already transformed the input and array to lowercase.
var input = "word";
var words = ["word", "words", "wordly", "not"];
var found = words.some(function(elem) { return elem.indexOf(input) != -1; });
Alternatively, you could skip in this instance transforming the array to be all lowercase by calling toLowerCase() on each elem before you check indexOf().
some() and map() aren't supported in older IEs, but are trivial to polyfill. An example of a polyfill for each is available at the linked documentation.
As Fabrício Matté also pointed out, you can use the jQuery equivalents here, $.map() for Array.prototype.map() and $.grep() with length property for Array.prototype.some(). Then you will get the browser compatibility for free.
To check if an array contains an element, case-insensitive, I used this code:
ret = $.grep( array, function (n,i) {
return ( n && n.toLowerCase().indexOf(elem.toLowerCase())!=-1 );
}) ;
Here is a fiddle to play with
array match case insensitive

Categories

Resources