Why should I use .forEach() vs a for loop?

Question

Why should I use .forEach() vs a for loop?

Answer

Some benefits of for loops:

  • Control over the condition of the for loop - ex: does not have to stop at the end of the array (i < array.length), can be a different condition
  • Can use break statements in a for loop to break out of the loop early, for example:
let myArray = ['this', 'is', 'my', 'array'];

for (let i = 0; i < myArray.length; i++) {
  if (myArray[i] === 'is') {
    console.log('critera is met!');
    break; //will break out of the `for` loop early, no more iterations of loop unnecessarily 
  }
}

Some benefits of .forEach():

  • Less setup - only need to pass a function to .forEach() to execute for each element of the array
  • Easy to read, especially with more complex code
  • Less room for error, for example, we will not run into the error where we iterate over an array one too many times
2 Likes

How come that the argument of the function politelyDecline was automatically known as it was given as an argument in the politelyDecline function, how did the forEach knew that the politelyDecline had an argument and that it should fill it with veggies array?:

const veggies = [‘broccoli’, ‘spinach’, ‘cauliflower’, ‘broccoflower’];

const politelyDecline = (veg) => {

  console.log('No ' + veg + ' please. I will have pizza with extra cheese.');

}

// Write your code here:

const declineEverything = (strArray) => {

strArray.forEach(politelyDecline);

};

declineEverything(veggies);

5 Likes

This is because politelyDecline is a variable that points to a function. This means that

strArray.forEach(politelyDecline);

is equivalent to:

strArray.forEach((veg) => {console.log(‘No ’ + veg + ’ please. I will have pizza with extra cheese.’});

4 Likes

This behavior is as a result of the syntax for .forEach().
In the syntax for .forEach(), It expects to be passed a callback function which has between 1 to 3 parameters. In order for .forEach() to work it requires the callback function passed in to match this syntax.
Out of the 3 callback function parameters, only one is compulsory, which is currentValue. The other two parameters index and array can optionally be passed in.

If the callback function we supply to the .forEach() doesn’t match the syntax, it will not work as intended
:slightly_smiling_face:

5 Likes

I am actually having a bit of a hard time understanding this logic, and the explanation did not seem to clear up the confusion that I am having, and hoping for some further clarity.

I understand that the variable ‘politelyDecline’ was passed to the .forEach() as an argument, but ‘politelyDecline’ is a function that takes one argument itself. When it was passed as an argument to .forEach() we never passed it any arguments? further more how was each ‘veg’ passed through to it when it was only passed a function variable?
It is this that gets me puzzled and reading the MDN documentation does not clear this up for me, as this example of usage does not seem to be discussed.

The syntax I would have used which was incorrect for this example as per the text in the question was ‘strArray.forEach(veg => politelyDecline(veg));’

That syntax makes sense to me as .forEach parses one ‘veg’ from the array and uses the call back of the politelyDecline passing it each ‘veg’ as its parameter.

If someone is able to please better clarify how this works that would be greatly appreciated.

2 Likes

Iterators such as, Array.forEach() have their own internal iterator variable. The callback need only be a reference to a function object. The iterator handles the invocation of the function, once for each value in the array.

4 Likes

Appreciate your time to reply.
Yes, this actually started making sense as I proceeded through that sections exercises, as the other array methods we learnt about act the same way.
I think the problem I was having is not fully understanding the documentation syntax correctly and piecing it together in my head.
Thanks again.

1 Like

I. am the same, I thought I had it then when I saw the second part where one has to pass another function to the accept vegies it shows an (e) is this because the function is already taken? I thought you can recall the function at any time? thus I had:
const politelyDecline = (veg) => {
console.log(‘No ’ + veg + ’ please. I will have pizza with extra cheese.’);
}
and

onst acceptEverything = (veg) => {
console.log(‘Ok, I guess I will eat some [veg].’);
}
I figured this was a simple plug and play.

HI Codecademy, please make you question more confusing so we can waste more time on triviality.keep it up!!!

1 Like

Just experienced this exact issue as well. Still trying to wrap my head around how a function can be called without an argument hahah.

1 Like

Have you learned about classes, yet? That is how a function call without an argument is possible. It has an execution context with a method. .forEach() is a method of the Array class. All instances of arrays have this method in their prototype chain.

We can learn about all the methods inherited by a class instance if it is built in. For instance MDN will have an Array page that details all the available methods (and properties) for this class of object.

Ever wonder why an object has no length? Because Object doesn’t inherit that property, and it is pointless to have a class method for something so trivial to objects represented as associative arrays. The Object.keys() method returns an array, which does have a .length property. Arrays and Strings have a length property they inherited from, drum roll, Object.prototype.

const nums = [
    'zero',
    'one',
    'two',
    'three',
    'four',
    'five',
    'six',
    'seven',
    'eight',
    'nine',
    'ten'
]

We have an array, and we know that an array instance inherits the forEach() method from its parent class’s prototype.

nums.forEach(function(x, i, a) {
    a[i] = x.toUpperCase();
}
 > console.log(nums)
   ['ZERO', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE', 'TEN']
<- undefined

Array.forEach() doesn’t need to return anything because it is able to execute directly upon the object (in place). That’s why we give it a callback function as an argument so it knows how we want the object manipulated or mutated.

//                 value   local name for `nums`
//                    \     |
nums.forEach(function(x, i, a) {})
//                      /
//                  index

Array.forEach() scans the entire array. It does not allow ‘break’. The iterator callback function has three parameters in the above example. One for the value, one to point to the index of that value, and one to give the context array a local name. It is because we have this third parameter that we have a pipeline back to the context object.

Erm, what was the question?

2 Likes

Haven’t learnt classes yet but look forward to doing so! Thanks for the insight.

2 Likes

You’re welcome. Be sure to do some follow up reading that either confirms or refutes what I said about Object.length. Now I read it I’m scratching my head as to the veracity or accurateness of that statement. It was a bit off the cuff and I definitely don’t claim to be an authority, more citing from distant memory.

Also, small note, there is an error in the example (a missing ) at the end).

The documentation for that method is not the easiest to plow through. Every time I pull .forEach out the box to play with, I learn something new. Go at all this stuff with an open mind and eventually you will learn everything that the language promises (for that method) and how to implement each one, even if just as an example such as above.

Happy coding!


Ah, heck, one more example to demonstrate a useful process:

 > indices = {}
<- {}
 > nums.forEach(function(x, i, a) {
       this[x] = a[i].toLowerCase();
   }, indices)
<- undefined
 > indices
{
  EIGHT: "eight",
  FIVE: "five",
  FOUR: "four",
  NINE: "nine",
  ONE: "one",
  SEVEN: "seven",
  SIX: "six",
  TEN: "ten",
  THREE: "three",
  TWO: "two",
  ZERO: "zero"
}
// expanded for better illustration
1 Like

I’m struggling too. If I’m honest, I raced through some of the content knowing full well it would bite me later. Reading through posts like this are really helpful and just wanted to say thanks for re-wording some dense Codecademy teaching whilst still using the jargon (…there’s probably a loop out there for that somewhere!). Sometimes it just takes reading it from a different angle.
:melting_face:

1 Like