The moment I realized forEach() does not return anything.
Tags: javascript, algorithms, beginners
Published: 2019-08-31 22:06:19

**Javascript ES5's `forEach()` method and I had been getting along quite well until last week when I was trying to implement it inside this one simple function that takes in two arrays and decides whether they contain common items.**

##**One of my two `forEach()` loops refused to do its job**

So there I had two test arrays to begin with:

```javascript
const animals = ["lion", "dog", "kangaroo"];
const pets = ["hamster", "parrot", "dog", "cat"];

haveCommonItems(animals, pets); // should return "true"
```

Apparently the two inputs both contain `"dog"` so my function on being called should be able to return `true` in this case (and `false` in an hypothetically opposite one). For the sake of [time complexity optimization](https://en.wikipedia.org/wiki/Time_complexity), I decided to give it two independent loops (instead of nested ones), using Javascript ES5's `Array.prototype.forEach()` method:
```javascript
const haveCommonItems = (array1, array2) => {
  array1.forEach(
    // converts array1 items into properties
    // returns an object that has those properties
  );

  array2.forEach(
    // checks if array2 items equal to properties on object created from first loop
    // returns a boolean
  );
}
```
My first `forEach()` loop does its job by yielding the expected object, like so:
```javascript
const haveCommonItems = (array1) => {
  let obj = {};
  array1.forEach(item => !obj[item] ? obj[item] = true : null);
  console.log(obj);
};

haveCommonItems(animals); // logs {lion: true, elephant: true, dog: true, kangaroo: true}
```
Things started to confuse me as my second `forEach()` came along. On running the function I was expecting to get back `true` which never happened; instead I got a `false`:
```javascript
const haveCommonItems = (array1, array2) => {
  let obj = {};
  array1.forEach(item => obj[item] ? null : obj[item] = true);

  array2.forEach(item => {
    if (obj[item]) {
      return true;
    }
  });

  return false;
};

haveCommonItems(animals, pets); // returns "false" (me: "c'mon gimme a break!")
```
Apparently it seemed like either my `array2.forEach()` never actually looped through the array itself, or it did but it was just being selfish and not giving back anything.

_I was right about the latter._

##**What actually was going on inside of that so-called loop?**

I did a little research by jumping into the [MDN web docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) only to figure out this one major feature that I had paid almost no attention to before:
>###`forEach()` always returns the value `undefined` and is not chainable.

_and_

>###There is no way to stop or break a `forEach()` loop other than by throwing an exception.

It turned out I had been under the wrong impression that `forEach()` was there to completely take the job over from my old friend `for()` loop. In fact, `forEach()` takes in a `callback` function that does return a value, but **only** within the scope created by `forEach()`. In other words, the return statement inside that `callback` **never** brought the value out of its scope or exited the function; it only returned that value to `forEach()` which then continued to traverse the rest of the array before returning `undefined` to its boss `haveCommonItems()`. 

My function `haveCommonItems()` at this point (on getting that `undefined` value) had no idea what happened inside that `forEach()` (poor little guy!), so it continued on to reach its own return statement and gave me a `false`.

So yeah, while that AHA moment still lasted, I came up with somewhat a solution for it:
```javascript
const haveCommonItems = (array1, array2) => {
  let obj = {};
  array1.forEach(item => obj[item] ? null : obj[item] = true);

  let found = false;
  array2.forEach(item => {
    if (obj[item]) {
      found = true;
    }
  });
  return found;
};

haveCommonItems(animals, pets); // returns "true" (me: "you better do!")
```

Javascript ES5's `forEach()` method, although always return `undefined`, does execute side effects. For that reason, it is able to set the value of `found` to `true` once the item is equal to any of the `obj` properties. (**Notice** that `forEach()` continues to loop till the end of the array because it can't be stopped or broken unless there is an exception being thrown in).

So here I am, jotting this down hoping not to be fooled by a `forEach()` loop ever again.

---
##**Updated**
Thanks to awesome recommendations by my dev fellows below I have levelled up my code which goes like so:
```javascript
const haveCommonItems = (array1, array2) => {
    const array1set = new Set(array1);
    return array2.some(item => array1set.has(item));

haveCommonItems(animals, pets); // returns "true"
}
```
Thanks again guys!