Iterate over RegExp.exec(...) - javascript

From the RegExp.exec page on MDN, it gives the following example of iterating through a match with the g flag set:
const regex1 = RegExp('foo*', 'g');
const str1 = 'table football, foosball';
let array1;
while ((array1 = regex1.exec(str1)) !== null) {
console.log(`Found ${array1[0]}. Next starts at ${regex1.lastIndex}.`);
// expected output: "Found foo. Next starts at 9."
// expected output: "Found foo. Next starts at 19."
}
I have two questions about this code. The first one is why the !=== null is used here, why wouldn't the while loop be properly coded as:
while (array1 = regex1.exec(str1)) { // implicitly casts to boolean?
console.log(`...`);
}
The above seems much more readable to me, but was wondering why the MDN docs used the first approach? Second, is it possible to declare and define the variable directly in the while loop? Something like:
while (let array1 = regex1.exec(str1)) { // don't need the `let array1` above?
console.log(`...`);
}
Or is that not supported in the JS language?

Why the !== null is used here...?
True, in this case it is not needed. .exec() returns either an array or null, and since arrays are always truthy there is no need to explicitly compare with null.
Is it possible to declare and define the variable directly in the while loop?
No. If you want that, then turn to the for loop, which does support this:
for (let array1; array1 = regex1.exec(str1); null) {
console.log(`...`);
}
This does indeed have the advantage that array1 has a more restricted scope. NB: I provided null to stress that the third part of the for header is intentionally unused.

Related

You should iterate over the elements of the data array and print to the console the number of values in the array that are not NaN in Javasacript

So here is my solution:
let data = [11, null, NaN, 'Hello', 24]
let check = 0;
for (item in data) {
if (isNaN(item) == false) {
check +=1
};
};
console.log(check);
How ever the answer comes five, even though it is 2. I believe isNaN() function gives true or false, so why is it not working?
The in in item in data checks the properties of an object. In an array, the keys are numbers, you have five entries, so you get five times not isNan().
You want to iterate the array, so it should be let item of data
for...in loops over the properties of an object. To loop over an array's elements, use for...of or Array#forEach.
For this specific case, Array#reduce is particularly appropriate.
let data = [11, null, NaN, 'Hello', 24];
let res = data.reduce((a, b) => a + !isNaN(b), 0);
console.log(res);
Your code returns 5 because for...in iterates over the array keys. And the array keys of data are the numbers between and including 0 and 4. Five in total, all of them are numbers.
There are multiple ways to write the code.
Using for...in, use item as an index in data (because this is what it is):
const data = [11, null, NaN, 'Hello', 24]
let check = 0;
for (item in data) {
if (isNaN(data[item]) == false) {
check += 1;
}
}
console.log(check);
Using for...of, item is a value from data and the rest of your code works fine:
const data = [11, null, NaN, 'Hello', 24]
let check = 0;
for (item of data) {
if (isNaN(item) == false) {
check += 1;
}
}
console.log(check);
Using Array.forEach() you get both the index and the value as arguments of the callback function. The value is the first argument, the index is not needed (but it is available as the second argument):
const data = [11, null, NaN, 'Hello', 24]
let check = 0;
data.forEach((item) => {
if (isNaN(item) == false) {
check ++;
}
});
console.log(check);
The callback function used by Array.forEach() changes a variable that is neither received as argument, nor a local variable of the callback function. The code is somewhat confusing this way and it is not properly encapsulated. While this is fine because there is not better option, here it can be done better using Array.reduce(). Array.reduce() seems to be closer to what the program wants to achieve than Array.forEach() (that is just a cleaner way to write the two for loops attended before).
const data = [11, null, NaN, 'Hello', 24]
let check = data.reduce(
(acc, item) => {
if (isNaN(item) == false) {
acc ++;
}
return acc;
},
0
);
console.log(check);
However, Array.reduce() is still a wrong fit. The purpose of the code is to count the numbers from the data array. A more natural way is to not count them but to extract the numbers into a new array using Array.filter(). The array itself tells us how many items it contains by accessing its .length property. There is no need for ifs:
const data = [11, null, NaN, 'Hello', 24]
const check = data.filter((item) => !isNaN(item)).length;
console.log(check);
Other remarks
Do not put semicolons (;) after the close brace } of a code block. The semicolon is a statement separator and a code block can be used wherever a statement can be used. All in all the following piece of code
if (true) {
};
... contains two statements. One is if (true) with an empty code block on the if branch, the other is an empty statement that follows the code block until the semicolon.
It does not have any undesired effect here but it can be the source of hidden bugs difficult to find.
The semicolon is required only after some JavaScript statements. For all the others, a new line works the same as a semicolon. More than that, the JavaScript interpreter is able to insert some missing semicolons automagically.
Attention! I do not say to never use semicolons. Use them where they must be used but be aware that a missing semicolon is either inserted by the interpreter or flagged as a syntax error (and the program does not start) while an extra semicolon in the wrong place can make your program behave in an unexpected way.
Do not use == and !=. They do loose comparisons (f.e. 2 == '2') and sometimes they have surprising results. Always use the strict equality operator (===) and the strict inequality operator (!==). They first check the types of the two operands and do not do confusing conversions to check the values.
x += 1 is usually written x ++. Be aware that, in some contexts, x ++ is has different side effects than ++ x.
There is nothing wrong with += 1, just nobody uses it.
Sometimes, using the logical NOT operator (!) the code becomes more clear. It is abused by many developers that produce "clever" but unclear code but using it here is better than comparing against false.
To me, if (! isNaN(item)) seems easier to understand than if (isNaN(item) === false). It can almost be read: "if item is not NaN" vs. "if item is not NaN is false".
It does not make any difference to the interpreter though. Write the code to be easy to read and understand. The computer does not care.

Using "OR" operator inside includes() to trial the existence of any substrings within a string?

I noticed when trying to use OR operators inside an includes() function like this
x.includes("dogs"||"cats"||"birds"||"fish"||"frogs")
It will only trial the first string contained, and no further. I suspect that i'm either missing something obvious here or includes() isn't the right function to be used for this kind of situation.
The goal is to trial multiple strings to determine if they are substrings of x. Because i'm trying to use or operators, my intent is to not receive an array of boolean values for each trial string, but rather if any are true, then a single boolean value of true, otherwise false is desired.
The || operator is not distributive. Function arguments are simply evaluated as expressions, so your call is equivalent to:
var temp = "dogs"||"cats"||"birds"||"fish"||"frogs";
x.includes(temp)
The value of a series of || operations is the first truthy value in the series. Since all non-empty strings are truthy, that's equivalent to:
var temp = "dogs";
x.includes(temp)
You need to use || on the result of calling includes for each string:
x.includes("dogs") || x.includes("cats") || x.includes("birds") ...
You can simplify this by using the some() method of arrays:
["dogs","cats","birds","fish","frogs"].some(species => x.includes(species))
includes only looks for one string. You can use .matchAll() function which returns an iterator of all matching results
const regex = /dogs|cats|birds|fish|frogs/g;
const str = 'the dogs, cats, fish and frogs all watched birds flying above them';
const exists = [...str.matchAll(regex)].length > 0;
console.log(exists);
For this case, with a regular expression and a wanted boolean result RegExp#test comes in handy.
This approach does not return an iterator and need no array for getting a length of it.
const
regex = /dogs|cats|birds|fish|frogs/g,
str = 'the dogs, cats, fish and frogs all watched birds flying above them',
exists = regex.test(str);
console.log(exists);
Here's what I use in this situation:
let searchString = "fish"
console.log(["dogs","cats","birds","fish","frogs"].includes(searchString))

Check that a list of variables have each been set

I have an array filled with the names of variables like this:
var myVariables = [variable1,variable2,variable3,variable4];
Is there a simple way besides and each to test if all of these variables have been assigned a value (elsewhere in my code)?
I would suggest using Array.some, with that approach there is a chance you won't have to iterate the entire array:
const hasEmpty = myVariables.some(v => typeof v === 'undefined');
You could use the Array.prototype.some() method :
The some() method tests whether some element in the array passes the
test implemented by the provided function.
It could be more efficient than forEach method as it stops iterating (short circuit in a some way) as soon a element matches the condition.
For example to check that all elements are > 0, use some() with the reverse condition, that is : <=0.
var isFailed = [0, 1, 2, 3, 4].some(x => x <= 0);
For example, here, as soon the first iteration, some() exits and return false.
return myVariables.indexOf(undefined) === -1;
If one of your variable is not defined, this will throw a ReferenceError such as mentionned in top comments by Felix Kling. Otherwise if it has the value undefined then you can check if your array contains undefined values.
don't throw Reference Error
If you execute the code below you will get a ReferenceError since variable1 has never been defined.
const myArray = [variable1]
But this next code will just create an array with an undefined value in it, since the variable is declared:
let variable1
const myArray = [variable1]
check if undefined is included
With ES 2016 includes():
const isAllDefined = !myVariables.includes(undefined)
// use this boolean where you need it

getting a value from an array or a single value

I want to extract a value from a variable. If its an array, I want the first element, and if not, I want the value of that variable. The value is a float, but I was wondering which of these are better in terms of performance, portability to non-floats, and of course short code and code readability
I want to use
value = variable[0] || variable
Its short and nice, is there any caveats is using this?
or I can do,
value = ([].concat(variable))[0]
This SO question says it's bad for performance.
And then, ofcourse, I can check if variable is an array or not in a few different ways, that is also mentioned in above question. Is there any better ways if the first one is not good?
Your value = variable[0] || variable will work, and will work reliably. Technically, if variable is a number primitive, what the JS engine has to do at that point is promote it to an object and then look up the 0 property on that object, but as you know it won't have one, that's okay.
The only cases where that may fail are if variable is null or undefined, because neither of those can be promoted to an object, and so the expression will throw. Provided you're okay with that, then you can use that. I'd comment it, though, because it's pretty odd-looking.
If you needed to defend against null and undefined, you could use the fact that == null will filter out both of those:
value = variable == null ? undefined : variable[0] || variable;
I'd comment that, too. :-)
Having this
value = variable[0] || variable
you may go to a trouble if the first element of the array is false. For example:
var arr = [false, 1, 2, 3];
value = variable[0] || variable; // <--- value is [false, 1, 2, 3]
So, I'll go with this:
var value = arr instanceof Array ? arr[0] : arr;
If you are not sure if the array is full then you should add one more check.
var value = arr instanceof Array && arr.length > 0 ? arr[0] : arr;

Calling javascript function with an objectstring in dot notation

Suppose I have the string:
var string = "function";
With
window[string];
I can call a function with the name of "function".
But, when I have:
var string2 = "function.method.weHaveTogoDeeper";
it should call
window["function"]["method"]["weHaveTogoDeeper"]
I can't do:
window[string2]
in this case. I dont know the number of "." in the string, so I need some kind of routine.
you can split the string across . by using the String.split method:
var string2 = "function.method.weHaveTogoDeeper";
var methods = string2.split(".");
In this examples, methods will be the array ["function","method","weHaveTogoDeeper"]. You should now be able to do a simple iteration over this array, calling each function on the result of the previous one.
Edit
The iteration I had in mind was something like this:
var result = window;
for(var i in methods) {
result = result[methods[i]];
}
In your example, result should now hold the same output as
window["function"]["method"]["weHaveTogoDeeper"]
function index(x,i) {return x[i]}
string2.split('.').reduce(index, window);
edit: Of course if you are calling functions from strings of their names, you are likely doing something inelegant which would be frowned upon, especially in a collaborative coding settings. The only use case I can think of that is sane is writing a testing framework, though there are probably a few more cases. So please use caution when following this answer; one should instead use arrays, or ideally direct references.
I wrote one a while back:
function RecursiveMapper(handlerName, stack) {
// check if empty string
if(!handlerName || handlerName === '' || (handlerName.replace(/\s/g,'') === '')) return null;
var buf = handlerName.split('.');
stack = stack || window;
return (buf.length === 1) ? stack[buf[0]] : this.RecursiveMapper(buf.slice(1).join('.'), stack[buf[0]]);
}
Call it like this: RecursiveMapper(window[string2]);
This one also checks if the function is defined in window scope first and returns the global one fi found.

Categories