Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

javascript

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?

like image 239
msmith1114 Avatar asked Apr 22 '17 06:04

msmith1114


People also ask

What happens if you return in a forEach?

Using return in a forEach() is equivalent to a continue in a conventional loop.

Does return exit forEach?

This return statement does not exit the function In the code above, the return statement inside the forEach is not sent back to the caller function, rather the default return is sent back.

How do I loop through an array in forEach?

array. forEach() method iterates over the array items, in ascending order, without mutating the array. The first argument of forEach() is the callback function called for every item in the array. The second argument (optional) is the value of this set in the callback.

Why is the return value for .forEach undefined?

The "Cannot read property 'forEach' of undefined" error occurs when calling the forEach method on an undefined value. To solve the error make sure to initialize the variable to the correct value and only call the forEach method on the correct data type.


4 Answers

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
} 
like image 181
Blockost Avatar answered Oct 03 '22 20:10

Blockost


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.

like image 28
Tomalak Avatar answered Oct 03 '22 20:10

Tomalak


[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.

like image 33
C J Avatar answered Oct 03 '22 22:10

C J


function truthCheck(collection, pre) {
  return collection.every(function (person) { return !!person[pre]; });
}
like image 45
Jorge Gonzalez Avatar answered Oct 03 '22 21:10

Jorge Gonzalez