Angular inner foreach break/continue unexpected behaviour - javascript

I have a nested foreach which I want to break out of if a condition is met. It seems that using a return means that the execution breaks out of the outer foreach, not the inner:
angular.forEach(objectA, function(objA, key) {
angular.forEach(ObjectB, function(objB, key) {
if(foo !== bar) {
return;
}
});
});
I read that it is better to use s native for each for this purpose, so I created this fiddle which works as I need it to:
https://jsfiddle.net/3dfx14nt/
However when I apply the logic to my Angular app it behaves in the same way as the Angular.foreach does. When returning on the inner foreach it breaks out to the outer foreach.

forEach accepts a function and runs it for every element in the array. You can't break the loop. If you want to exit from a single run of the function, you use return
forEach doesn't support break, you should use return
If you want to be able to break the loop, you have to use for..of loop:
for(let name of group.names){
if (name == 'SAM') {
break;
}
}

Related

JavaScript - For loop vs forEach

can someone explain how these two pieces of code achieve the exact same thing* or explain why they may appear to but do not?
for (i = 0; i < xx.length; i++) {
xx[i].classList.remove(...y);
}
xx.forEach(x => {
   x.classList.remove(...y);
});
forEach is a method on the Array prototype. It iterates through each element of an array and passes it to a callback function.
So basically, forEach is a shorthand method for the use-case "pass each element of an array to a function". Here is a common example where I think Array.forEach is quite useful, compared to a for loop:
// shortcut for document.querySelectorAll
function $$(expr, con) {
return Array.prototype.slice.call((con || document).querySelectorAll(expr));
}
// hide an element
function hide(el) {
el.style.display = 'none';
}
// hide all divs via forEach
$$('div').forEach(hide);
// hide all divs via for
for (var divs = $$('div'), i = 0; i < divs.length; i++) {
hide(divs[i])
}
As you can see, the readability of the forEach statement is improved compared to a for loop.
On the other hand, a for statement is more flexible: it does not necessarily involve an array. The performance of a normal for loop is slightly better, because there is no function call for each element involved. Despite of this, it is recommended to avoid for loops when it can be written as a forEach statement.
Careful, if you declare "i" without "var" keyword it will be global.
You can check what forEach can do here:
forEach MDN

for loop syntax

Someone can explain me this part of code please.
function parent (elem, selector ) {
for ( ; elem && elem !== document; elem = elem.parentNode ) {
if ( elem.matches( selector ) ) return elem;
}
}
I understand what he does, but I don't understand the start of loop exactly this part : for ( ; elem && elem !== document; ...
Usually we have to specify a starting number for the iteration, it's very strange for me this loop.
Thanks for your help.
While the first part of the loop configuration is for setting up an index, the second part is where the looping condition goes. If the condition is not based on the index, then the index can be omitted. In this case, the loop cares about the passed argument elem, not an index.
In fact, all 3 configuration sections of a for are optional (making the for more like a while). From MDN:
Syntax
for ([initialization]; [condition]; [final-expression])
statement
(As you may be aware, the use of [] in an argument list being show for syntax purposes means optional.)
Here's an example:
var obj = {}; // Not null
// Keep looping as long as obj is not null (which it isn't to start)
for ( ; obj != null; obj=null) {
console.log("Loop!"); // First time through the loop, obj != null so loop will go
} // After first iteration, obj will become null (because of 3rd part of loop config)
// so loop will terminate
Having said that, this is certainly not an elegant way to use JS for syntax. The logic could be handled in a more readable way with a while loop:
function parent (elem, selector ) {
while (elem && elem !== document){
if (elem.matches( selector )) {
return elem;
}
elem = elem.parentNode;
}
}
There's nothing that specifies a for loop must use an iterator. The for loop syntax was invented because it is very common to iterate over a range of numbers, and doing that with a while loop always looks something like this:
var counter = 0;
while (counter < 10) {
// do something...
counter++;
}
Because it was so common to see a variable used as an iterator, then an expression to be checked in a while loop, and an increment operation at the end of the while loop body, some clever programmers created the for loop syntax so that these parts could all be stated upfront in a concise manner: for ([initialization]; [condition]; [final-expression]). Note well that any of these parts can actually be omitted - only the semi-colons are actually required inside the parenthesis.
The author of this code doesn't need an iterator, however, perhaps he or she just likes that all of the necessary pieces are on a single line. You could just as easily write this function with a while loop that would look like this:
function parent(elem, selector) {
while (elem && elem !== document) {
if (elem.matches(selector)) {
return elem;
}
elem = elem.parentNode;
}
}
You may refer to this. The structure of a for-loop consists of three statements, all of which are optional. The first statement executes before the loop starts, so typically, it is used for variable declaration. The second statement is used to define conditions. The third statement Is called upon each loop, hence it is typically used to increment or decrement values, or assign new values.
When all of these statements are not there as below, it means that it is an infinite loop.
for (;;)
When the first statement is not there, which is the case in your example, then there was need to declare variables or have a code block to execute before the for loop starts. However, there is still condition that must be satisfied in order to keep the loops going (statement 2), and block that is called upon each loop (statement 3).
Statement 1: None // If there was any, it would have been called at the beginning
Statement 2: elem && elem !== document // Decides whether to continue looping
Statement 3: elem = elem.parentNode // Called each loop

forEach/for...in not returning values? [duplicate]

This question already has answers here:
Function with forEach returns undefined even with return statement
(5 answers)
Closed 3 years ago.
So I have a little bit of confusion, I'm solving a challenge on freeCodeCamp.
The challenge reads as follows
Everything Be True
Check if the predicate (second argument) is truthy on all elements of a collection (first argument).
It's solved but I don't understand why I had to take an extra step. My code is as such:
function truthCheck(collection, pre) {
collection.forEach(function(element) {
for (key in element) {
if (!element.hasOwnProperty(pre)) {
return false;
} else if (key === pre) {
if (!Boolean(element[key])) {
return false;
}
}
}
});
return true;
}
truthCheck([
{"user": "Tinky-Winky", "sex": "male"},
{"user": "Dipsy"},
{"user": "Laa-Laa", "sex": "female"},
{"user": "Po", "sex": "female"}
], "sex");
so in this instance it should fail because the 2nd element within collection does not have the sex property. Also you will receive a fail if the pre argument, or in this case sex is not a truthy value.
When these get hit (Which they are, I'm able to tell through console logs) but I figure it would break out of the loop and return from the truthCheck function.....but it doesn't, and it will eventually return true.
I was able to circumvent this by defining a variable and then setting that value to false and then returning the variable at the end. Is there a better way? It seems like these returns should break out of the truthCheck function? Am I missing something?
As the other answers explain, this is meaningless:
collection.forEach(function () {
// do something
return false;
});
because array#forEach simply does not care for the return value of its worker function. It just executes the worker function for each array element.
You could use the worker function to set an outer variable:
function truthCheck(collection, pre) {
var allAreTruthy = true;
collection.forEach(function (elem) {
// if this ever flips allAreTruthy to false, it will stay false
allAreTruthy = allAreTruthy && elem[pre];
});
return allAreTruthy;
}
But there are better ways to express this.
Check if the predicate (second argument) is truthy on all elements of a collection (first argument).
Could be paraphrased as "Every element of the collection has a truthy value at a particular key."
function truthCheck(collection, pre) {
return collection.every(function (elem) { return elem[pre]; });
}
Could be paraphrased as "None of the elements of the collection have a falsy value at a particular key (or are missing the key entirely)."
Or, since an Array#none method does not actually exist, "There aren't some elements of the collection that have a falsy value at a particular key."
function truthCheck(collection, pre) {
return !collection.some(function (elem) { return !elem[pre]; });
}
The advantage of using Array#some is that it stops iterating the array as soon as the condition it seeks for is fulfilled. If your array had many elements this would mean improved performance. For short arrays there's not a lot of difference to using Array#every or Array#forEach.
The above is semantically equivalent to
function truthCheck(collection, pre) {
var i;
for (i = 0; i < collection.length; i++) {
if (!collection[i][pre]) return false;
}
return true;
}
Since JS objects simply return undefined when you access a key that has not been set, a check for hasOwnProperty is superfluous here.
You can't return anything from a ForEach loop. It will return undefined by default.
As the official documentation, Array.prototype.forEach() - JavaScript | MDN, says:
There is no way to stop or break a forEach() loop other than by throwing an exception. If you need such behavior, the forEach() method is the wrong tool, use a plain loop instead. If you are testing the array elements for a predicate and need a Boolean return value, you can use every() or some() instead.
So you can use a very simple for..in loop, for example:
for(var c in collection){
// Do whatever you want
}
[collection].forEach of javascript does not work like an ordinary loop. There is no way to prematurely end it not unless you make it throw an exception.
The behavior that you are expecting is what you would expect out of a javascript for loop but since forEach uses callback functions for each looped object, then you are only exiting the callback function instead of the forEach. Also it is worth noting that in your code, the you have a for loop which has a return in it. The return block in this loop only breaks this loop and not the forEach (which I have mentioned earlier, cannot be prematurely terminated unless otherwise)
As you can see forEach is mostly meant to iterate all elements instead of conditional checks per iterated element.
function truthCheck(collection, pre) {
return collection.every(function (person) { return !!person[pre]; });
}
you execute a function on each element of the collection. This function checks that element an returns something. But that returned value does not affect the result of the outer function. Since the outer function does not depend on the inner function your result is allways true.
if you define a variable, set this to false and return that variable at the end would works but it would be inefficient. Lets think of the following scenario. You found one element which does not has the target key. So right now you should return but you can't. You have to working yourself through the whole collection. The forEach loop does not give you the chance to exit without a mess. Thus a better idea is the for loop. Your can exit exit the for loop if found what you were looking for
a slightly easier way would be:
function truthCheck(collection, pre) {
//iterate thrugh your collection
for (var c in collection){
//get keys of element as an array and check if pre is in that array
if( Object.keys(collection[c]).indexOf(pre) == -1){
// pre was not found
return false;
}
}
return true;
}

Looping through array in if statement to check if all values pass

I have a variable 'fieldCount' that is equal to 5 (the number of fields I have). How can I write the following out without specifically stating each index:
if (
fields[0].checkValidity() && fields[1].checkValidity() && fields[2].checkValidity()
&& fields[3].checkValidity() && fields[4].checkValidity()
) {
fieldsPass = true;
}
You can use a loop, or some fairly standard built-in browser functions.
Loop
Here's how to do it with a loop. This should be the fastest code, but you probably don't need the fastest code unless you are checking a huge number of fields on a huge number of items. I recommend instead the "correct, clear, concise, fast" priorities, so think you should start with the built-in browser functions. But here it is for reference:
var fieldsPass = true;
var i, l = fields.length;
for (i = 0; i < l; i += 1) {
if (fields[i].checkValidity()) continue;
fieldsPass = false;
break;
}
Note that declaring the i variable outside of the loop and capturing the field length outside the loop are optional.
The first I did because many people don't know about hoisting, and the fact that for (var i ... does NOT create a variable only available inside the for loop, it is the same as having declared var i at the top of the function, and this behavior can lead to bugs.
The second I did out of habit, though as I said you can put it inside the loop check. See this discussion. If you do use the loop method, you're probably looking for better performance, so might want to use the captured length way for the best possible performance. (And if it really matters that much, you can do the var i = fields.length; while (i--) { } method.)
Browser Function Array.Every
You can use Array.prototype.every() (from ECMAScript 2015 6th edition):
Mozilla Developer Network
The every() method tests whether all elements in the array pass the test implemented by the provided function.
fieldsPass = fields.every(function(field) {
return field.checkValidity();
});
It returns truewhen the passed-in function, when run on every item in the array, returns true for all of them. If any one returns false, it stops and returns false. In some languages, they call the same concept all, if you're familiar with that.
Alternately, it might be better to declare your checkValidity function once, instead of putting it on every field. This may not be possible, depending on how you implemented it (perhaps it has access to private variables?). Notice that the first argument of the callback function you provide (see the documentation in the links above) is the currentValue of the iteration, the field you want to check. If your function thus looks like this, it will work:
function checkValidity(field) { /* check validity */ }
fieldsPass = fields.every(checkValidity);
Browser Function Array.Some
You can use also Array.prototype.some() (from ECMAScript 2015 6th edition):
Mozilla Developer Network
The some() method tests whether some element in the array passes the test implemented by the provided function.
fieldsPass = !fields.some(function(field) {
return !field.checkValidity();
});
Notice that it is basically just the inverse of every, as "ALL VALID" is the same as "NOT (ANY INVALID)". It just means that it checks for any one item in the array that passes the function, and if so, returns true. In some languages, they call the same concept any, if you're familiar with that.
General Browser Compatibility Notes
Note that for these two functions, browser compatibility is pretty good. If you don't care about IE below version 9 then you're pretty much safe. If you do, then you would want to use a polyfill, which is available on the above-linked MDN pages. You would include that code in the global scope of your javascript file, and then would be able to use it as normally in IE 8 and below. (I'm talking about the code block starting like this:)
if (!Array.prototype.every) {
Array.prototype.every = function(callbackfn, thisArg) {
...
}
You have an array of fields, and you want to iterate them, and invoke a validity check method on each of them. Only if all of them pass, the flag fieldsPass would be true.
This is exactly the behavior of Array#every. According to MDN:
The every() method tests whether all elements in the array pass the
test implemented by the provided function.
Use Array.every to invoke checkValidity() on each field. If all fields are valid, the result would be true. If one field fails checkValidity(), the loop would return false immediately, without checking the other fields.
var fieldPass = fields.every(function(field) {
return field.checkValidity();
})
Like you said, use a loop.
var fieldsPass = true;
for (var i = 0; i < fields.length; i++) {
// Exit early if one of them fails
if (!fields[i].checkValidity()) {
fieldsPass = false;
break;
}
}
You can use the .some() method and reverse the logic to tell if all passed like so:
var allPassed = !fields.some(function (field) {
return !field.checkValidity();
});
You can do it like this:
fieldsPass = true;
for (i = 0; i < fieldCount; i++)
{
if(!fields[i].checkValidity())
{
fieldsPass = false;
break;
}
}

a few questions when iterator the arraylike object in javascript

In my application,I need to do some iteration with the array-like object.
So I write a custom util method:
Util.forEach=function(iterator,callback,context){
if(iterator.length){
for(var i=0;i<iterator.length;i++){
var val=iterator[i];
callback && callback.call(context||null,val,i);
}
}
}
Then I meet some problems:
Suppose I have an array: var ary=[1,2,3,4,5];
1. how to break the loop?
For example, if I want to find if value '2' is included in the array,I may try this:
Util.forEach(ary,function(value,index){
if(value==2){
//find mached value,now I should break and return this value.
//but I can not use 'break' in this context.
}
});
2. remove value of the array when iterator
If I want to remove value '2' of the array,I may try this:
Util.forEach(ary,function(value,index){
if(value==2){
//delete it.
ary.splice(i,1);
// I wonder if this is the safe way?
}
});
Since in java we can not do this when iterate an array,it will cause the concurrency exception.
Though I run the above code without any error,but I am not sure if it is the best practice?
Generally, you can break out from a forEach-like method by using try-catch and throw.
var value = 'Not Found';
try {
Util.forEach(ary, function(value, index) {
if(value == 2) {
throw value; //<-- Throw the found result: Terminates current forEach
}
});
} catch (found) { // Catch the thrown result
value = found;
}
The first issue can be solved by checking the return value from the callback and stopping the iteration if it returns false. Then you can stop the iteration by returning false from the callback. Returning anything other than false (including returning nothing) will continue the iteration. jQuery uses this return value technique to cancel the iteration in their .each() iterator. Adding that into your code, it would look like this:
Util.forEach=function(iterator,callback,context){
if (iterator.length && callback) {
for(var i = 0; i < iterator.length; i++){
var val=iterator[i];
if (callback.call(context||window,val,i) === false) {
break;
}
}
}
}
In the MDN documentation for forEach, you can see a sample implementation.
On the second issue, this type of implementaation does not permit insertion or deletion of elements at or before the iteration point because that will cause some elements to get skipped in the iteration or some objects to get iterated multiple times. The obvious way around that would involve making a copy of the object before iterating which is not efficient when not needed.
how to break the loop?
You're not supposed to break from forEach. That's why it's called "for each", and not "for some". "Breakable" JS iterators are every() (stops when the callback returns false) and some() (stops when the callback returns true).
Looking at your code once again it makes me think you actually want an indexOf kind of method, and not an iterator.
remove value of the array when iterator
Iterators shouldn't make any changes of the underlying array. You have to implement filter() and use it to generate a new array.
See js iteration methods for more details.

Categories

Resources