Why does .forEach() return undefined?

Why is the return value for .forEach() always undefined?

7 Likes
const fruits = ['mango', 'papaya', 'pineapple', 'apple'];

// Iterate over fruits below
fruits.forEach( fruit => {
  let indefiniteArticle = '';
  if (fruit[0] === 'a' || fruit[0] === 'e' || fruit[0] === 'i' || fruit[0] === 'o' || fruit[0] === 'u') {
    indefiniteArticle = 'an';
  } 
  //else if (fruit[0] === 'h'&& fruit[1] ===) { }
  else {
    indefiniteArticle = 'a';
  }
  console.log(`I want to eat ${indefiniteArticle} ${fruit}`);
});

/* Added indefinate article conditions. 
For future senerio, implment conditions with words starting with an H.
Also obeying DRY, create an array of vowels to loop through to avoid rewriting each vowels condition on the H comparison. */

I included Indefinate Article conditions to make an apple grammatically correct.

17 Likes

why would this method need to return anything? What matters are the values you get from the array

8 Likes

Hi
MDN and the instruction in the Codecademy say that .forEach() always return undefined!!
The question is why and even how we can get this while in codes Iā€™ve seen, it always return something but undefined!

2 Likes

Array.forEach() does return undefined, but never anything else. It iterates over an array and acts upon its elements in the manner we instruct by way of our callback.

Letā€™s demonstrate thisā€¦

 > function print (x) {
     console.log(x);
     return 0;
   }
<- undefined
 > [1,2,3,4,5,6,7,8,9].forEach(print)
   1
   2
   3
   4
   5
   6
   7
   8
   9
<- undefined

The last line is the echoed response to the command entered.

 > print([1,2,3,4,5,6,7,8,9].forEach(print))
   1
   2
   3
   4
   5
   6
   7
   8
   9
   undefined
<- 0

The 0 is the return from the print function. It is ignored by the iterator when the function is given as a callback.

 > console.log([1,2,3,4,5,6,7,8,9].forEach(print))
   1
   2
   3
   4
   5
   6
   7
   8
   9
   undefined
<- undefined

The last value is the logged return from .forEach().

12 Likes

What does it mean the: The return value for .forEach() will always be undefined

Thank you

1 Like
 > list = [1, 2, 3, 4, 5, 6]
 > console.log(list.forEach(x => x * 2))
   undefined
<- undefined
 > console.log(list.map(x => x * 2))
<- [2, 4, 6, 8, 10, 12]
<- undefined

One method has no return value, the other does.

5 Likes

Could you explain what you are trying to do here? Is this from the first lesson in the

The .forEach() Method

Thank you.

1 Like
console.log(`I want to eat ${`aeiou`.includes(fruit.charAt(0)) ? `an` : `a`} ${fruit}.`);

The above does the same thing as the earlier example without using if..else.

As in,

const fruits = ['mango', 'orange', 'papaya', 'pineapple', 'apple', 'banana', 'apricot'];
wantToEat = fruit => console.log(`I want to eat ${`aeiou`.includes(fruit.charAt(0)) ? `an` : `a`} ${fruit}.`);
fruits.forEach(wantToEat);
I want to eat a mango.
I want to eat an orange.
I want to eat a papaya.
I want to eat a pineapple.
I want to eat an apple.
I want to eat a banana.
I want to eat an apricot.
21 Likes

Because in this function we donā€™t use return. if you want to see undefined you can print:
console.log(fruits.forEach(oneFruit => console.log('I want to eat a ā€™ + oneFruit)))

1 Like

So basically the method will return undefined but it will also return the command that we set in stone right? Also why would I need to use a method that will give me undefined. What is its purpose and how to make full use of it?

2 Likes

what do you mean by this? a function can only a return one thing

undefined can mean you donā€™t have to do anything with the returned result.

1 Like

Not so much, ā€˜returnā€™, as carry out prescribed steps on each item in the array it is called upon. In as much, it is not far off from an ordinary for loop except that it is a higher order function that takes a callback, and it has no way of diversion (cannot use break).

Anytime we wish to perform an action on every item in an array this is a great iterator to reach for. The convenience of a callback which can take both a value and an index, along with optional pass-along arguments only adds to its utility.

Consider using it to enumerate a list (an array)ā€¦

a = ['zero', 'one', 'two', 'three', 'four', ]
b = []
a.forEach((x, i) => b.push([i, x])
console.log(b)
/*
[
  [0, "zero"],
  [1, "one"],
  [2, "two"],
  [3, "three"],
  [4, "four"]
]
*/

Now for some sillinessā€¦

const f = (x, i) => b.push([i, x])
const g = (x, i) => b.push([x, i])
a = ['zero', 'one', 'two', 'three', 'four' ]
b = []
a.forEach(this.length % 2 ? f : g)
console.log(b)
/*
[
  [0, "zero"],
  [1, "one"],
  [2, "two"],
  [3, "three"],
  [4, "four"]
]
*/
a = ['zero', 'one', 'two', 'three', 'four', 'five' ]
b = []
a.forEach(this.length % 2 ? f : g)
console.log(b)
/*
[
  ["zero", 0],
  ["one", 1],
  ["two", 2],
  ["three", 3],
  ["four", 4],
  ["five", 5]
]
*/
6 Likes

Okay so what is return mean what does it do? Is it always necessary I know that in arrow notation depending on the code written after the code you can either not add return or opt out of it. But Iā€™m not sure about itā€™s purpose in most cases.

1 Like

literally what it says, returning (handing back) data to the caller.

here is an example:

const example = () => {
  let abc = 'def';
  return abc;
}

result = example();
console.log(result);

without return, we wouldnā€™t be able to access the abc variable.

5 Likes

We donā€™t necessarily opt in or out, as much as accept that a single expression on one line without curly braces is implicitly returned. Note @stetim94ā€™s example above where there are multiple statements. There is nothing optional there. The curly braces must be used to define the block, and if a return is expected, then it must be written explicitly in the function body.

Technically speaking, our examples above donā€™t need a return since the action is taking place on a global object, the array, b.

We must never get into thinking that anything is ā€˜set in stoneā€™, as you put it earlier. There are any number of possibilities in terms of how we design our code. Just remember, we are the designers, and are not held to any gold standard apart from valid syntax. The logic is entirely up to us. When we wish to make use of the data created in a function then our best plan is to return that data to the caller. It can be stored at that point (and in that scope) and recalled later by another part of the program.

Our earlier callbacks can be written in conventional syntax with the exact same effect.

const f = function(x, i) {
  b.push([i, x]);
}

In either case, with or without a return it wonā€™t matter since forEach() does not expect a return (from the callback), nor will it process one in any event. The above function will return undefined. The earlier arrow function callbacks do return something, the new length of the array, which is inconsequential and ignored.

Of all the iterators, I believe forEach() is the only one that will return undefined. In cases where a return is needed then we would need to reach for one of the other iterators, such as map().

Bottom line, if return is still causing you some concern then the best I can suggest is go back to the beginning of the functions unit and review the narratives and exercises, and do a little digging in the documentation. Once you are clear on its use, you will be better able to design code and know when and when not to include it.

8 Likes

I want to know how .forEach know how to pass each element in fruits to function we refer to it.
I think it should work like this fruits.forEach(console.log(x)) ā€“ to print elements as x ā€” I think something is behind sense in js and this method , that it can automatically pass each element of array to given function. how ?

There is a pollyfil variable of the forEach method:

if (!Array.prototype.forEach) {

  Array.prototype.forEach = function(callback/*, thisArg*/) {

    var T, k;

    if (this == null) {
      throw new TypeError('this is null or not defined');
    }

    // 1. Let O be the result of calling toObject() passing the
    // |this| value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get() internal
    // method of O with the argument "length".
    // 3. Let len be toUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If isCallable(callback) is false, throw a TypeError exception. 
    // See: http://es5.github.com/#x9.11
    if (typeof callback !== 'function') {
      throw new TypeError(callback + ' is not a function');
    }

    // 5. If thisArg was supplied, let T be thisArg; else let
    // T be undefined.
    if (arguments.length > 1) {
      T = arguments[1];
    }

    // 6. Let k be 0.
    k = 0;

    // 7. Repeat while k < len.
    while (k < len) {

      var kValue;

      // a. Let Pk be ToString(k).
      //    This is implicit for LHS operands of the in operator.
      // b. Let kPresent be the result of calling the HasProperty
      //    internal method of O with argument Pk.
      //    This step can be combined with c.
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal
        // method of O with argument Pk.
        kValue = O[k];

        // ii. Call the Call internal method of callback with T as
        // the this value and argument list containing kValue, k, and O.
        callback.call(T, kValue, k, O);
      }
      // d. Increase k by 1.
      k++;
    }
    // 8. return undefined.
  };
}

allowing us to see how the function works.

Although the code is a bit complicated, we can clearly see a loop begin used.

3 Likes

Actually that was quite brilliant thanks.

2 Likes

Thanks so much, Roy, for your replies on this. It really helped me understand the difference between the idea of ā€œreturningā€ something vs using the data in a tangible way for the end user. I also understand the lesson better now ā€“ when to use .forEach() and when another method might better serve. I really appreciate the time and thought you put into your replies, and the artistry with which you approach coding. Itā€™s an inspiration :bowing_woman:

4 Likes