DEV Community

Cover image for "for" vs. "forEach" and the value of documentation
Arit Developer
Arit Developer

Posted on

"for" vs. "forEach" and the value of documentation

This post is a quick note to underscore how important it is for devs to get comfortable with documentation. Mind you, I'm the first one over on StackOverflow when I encounter problems while coding, and I've relished the ease of dev tutorial training-wheels (more on that here). But I am falling in love with the detailed, example-rich help that well-written docs can give.

Case in point: I recently wanted perform some calculations on each item of an array. So I coded the following:

array1 = [2,3,4,5];
function greaterThan(numbr){
    array1.forEach(function(item){
        if (item >= numbr){
            return true;
        };
    });
    return false;
};

greaterThan(4); // expect "true"
greaterThan(9); // expect "false"
greaterThan(2); // expect "true"
greaterThan(1); // expect "true"
Enter fullscreen mode Exit fullscreen mode

However, all my function calls above returned false. I resisted the urge to google "forEach not working" and went straight to MDN. Sure enough, in the docs for "forEach" I read the following:

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.

So simple. So straightforward. My return true line wasn't breaking the forEach loop; its execution continued to the end. I verified this by running greaterThan(5); it came back true since 5 was the last number in my array, and hence the last number processed. I rewrote my code using a simple for-loop and all was right with the world.

What are some ways that you have embraced documentation over other information sources?

Top comments (27)

Collapse
 
kip13 profile image
kip

You can do something more to keep this code structure, just a helper variable:

function greaterThan(numbr){
    let helper = false;

    array1.forEach(function(item){
        if (item >= numbr && !helper){
            helper = true;
        };
    });

    return helper;
};

But to keep the functional style that you want with forEach and stay in simple(readable?) code:

const greaterThan = (number) => array1.some((v) => v >= number)

Sometimes exists another method in the docs to get the goal that you want...

Collapse
 
aritdeveloper profile image
Arit Developer

Thank you!

Collapse
 
sait profile image
Sai gowtham • Edited

some method doesn't check the condition with every element present in the array it just returns true if one element satisfies the condition.

const array = [2,3,4,5];

function  greaterThan(number) {
 return  array.some((e) => e >= number)
}


console.log( greaterThan(1)); // true

Like in above code some method only checks the condition with first element in the array so it returns true .

There is a every method in javascript, it checks the condition with every element present in the array.

const array = [2,3,4,5];

function  greaterThan(number) {
 return  array.every((e) => e >= number)
}


console.log( greaterThan(1)); // true

References

Collapse
 
joelnet profile image
JavaScript Joel

If we break down the code, it is easier to see what is going on.

I'm gonna pull the function out of the forEach and create a new gte function.

function gte(a, b){
  if (a >= b){
    return true;
  };
  return false;
};

function greaterThan(numbr){
    array1.forEach(item => gte(numbr, item));
//                 ------------------------
//               /
// Now it's easier to see that this doesn't do anything.
    return false;
};

Array.forEach's first argument is a function. I would recommend always breaking the function out to make the code more understandable.

When written this way, it is more clear what is going on and that you would not expect the forEach to return a value from the outer function greaterThan.

I also agree with kip, the better way would be some or every since it will break early on a false and not iterate the entire array.

array1 = [2,3,4,5];
const greaterThan = numbr => array1.some(item => item >= numbr);

greaterThan(4); //=> true
greaterThan(9); //=> false
greaterThan(2); //=> true
greaterThan(1); //=> true
Collapse
 
aritdeveloper profile image
Arit Developer

Thank you so much for this!

Collapse
 
deciduously profile image
Ben Lovy

Shout out to the ReasonML and ReasonReact docs.

I built my entire project from blank file to production, starting with zero knowledge of Reason, without needing any resource at all outside of these two websites once. That's not something that happens too often.

Collapse
 
qm3ster profile image
Mihail Malo • Edited

I think it's helpful to understand why, regardless of what the forEach method actually does, the above couldn't work.
Even if we imagine that forEach short circuits on a truthy return value from the function passed in... and returns that value...
There's still no way that value would make it to the output of our greaterThan function.

const array1 = [2,3,4,5];
function equalOrGreaterThan(numbr){
    return array1.forEach(function(item){
        if (item >= numbr){
            return true;
        };
    })
    || false;
};

This would work (if that's what forEach did), since we are taking note of the return value. (The || false is there so that we know exactly what we're returning, not null or undefined)

Since the .findIndex() .find() .some() family of methods do exist, we can indeed do that:

const array1 = [2,3,4,5]
function equalOrGreaterThan(numbr){
    return array1.some(function(item){
        return item >= numbr
    })
}

Functions that have only one statement - a return statement, are also prime candidates to be arrow functions:

const array1 = [2,3,4,5]
const equalOrGreaterThanπŸ‘Œ = numbr =>
    array1.some(item => item >= numbr)
Collapse
 
qm3ster profile image
Mihail Malo

Finally, if doing this in real life, you probably want to avoid iterating on every check if the array is unchanged:

const equalOrGreaterThanπŸ‘Œ = array => {
    const max = Math.max(...array)
    return numbr => max >= numbr
}
const equalOrGreaterThanπŸ‘ŒπŸ˜‚ = array => {
    let max = Math.max(...array)
    return {
        isIt: number => max >= numbr
        push: (...args) => { max = Math.max(max, ...args)}
    }
}
Collapse
 
quantumsheep profile image
Nathanael Demacon • Edited

I see all the solutions in the comments, you can also do this with the filter method:

const greeterThan = num => array1.filter(n => n >= num).length > 0;

OR (basically the same)

function greeterThan(num) {
  return array1.filter(n => n >= num).length > 0;
}
Collapse
 
qm3ster profile image
Mihail Malo

Array.prototype.filter() is probably the least suitable here, for two reasons:
1) It allocates an output array.
2) Even if the very first element matches, it will still test all of the remaining elements.

In contrast, Array.prototype.some() only returns a boolean, and never executes the predicate past the first match. In other words, it implements the optimizations one would do in the for loop when does at least something match? is the question.

If you do care about the value, there's Array.prototype.find(), and if you actually need the key/index yourself, for example to replace the value in place or look up a matching index in another array, there's Array.prototype.findIndex()

Collapse
 
swarupkm profile image
Swarup Kumar Mahapatra

Wondering why you didn't use [12,3,44,5].find function for the same ?

function greaterThan(number) {
    return array1.find((item) => item > number ) !== undefined
}
Collapse
 
_andys8 profile image
Andy • Edited

In the example find is the solution that is the best description for the wanted behavior.

In general, if you "wanted perform some calculations on each item of an array", prefer map (a pure transformation of each object) over imperative for and forEach.

Collapse
 
swarupkm profile image
Swarup Kumar Mahapatra

Totally agree . People who start programming, generally they start with the procedural paradigm of programming. for loop is one the first thing that people come across (along with if-else). That sticks to their mind forever , until functional programming is introduced to them . Its just matter of time to get used to such paradigm . I am pretty sure with few weeks, the author of the article will be very well versed in functional programming

Collapse
 
aritdeveloper profile image
Arit Developer

What I'm loving about this thread is reading all the ways to accomplish my coding goal 😊 Thanks so much for your suggestion.

Collapse
 
swarupkm profile image
Swarup Kumar Mahapatra

Because of this reason I joined dev.to recently .

Back to the arrays discussion, the trend of using for loop will go away.
For every use case there must be some array function available.

w3schools.com/jsref/jsref_obj_arra...

Collapse
 
oathkeeper profile image
Divyesh Parmar

And one thing to remember for interviews is that none of them returns a new array ( or iterable ) only Array.prototype.map() returns a new array or iterable

this has what confused me a lot in few of my interviews from those harsh 73 interviews.

Collapse
 
qm3ster profile image
Mihail Malo

filter returns a new array as well.
And in cases where it's used as a map+filter optimization, so does reduce (but not in other cases).

Collapse
 
oathkeeper profile image
Divyesh Parmar

can you please elaborate on this more or give some references articles/blogs to read from?

Collapse
 
qm3ster profile image
Mihail Malo

Idk, they're really easy to find, I don't have a specific source.
This seems adequate enough: medium.com/jsguru/javascript-funct...

Collapse
 
webdva profile image
webdva

When I first started out using Angular, I developed a bias for its official documentation as it was very comprehensive.

Collapse
 
anurbol profile image
Nurbol Alpysbayev • Edited

Agree, I love how angular's docs written. The problem is Angular tries to cover a much broader set of problems, hence its overall harder for people and in the end, less popular.

Collapse
 
jakebman profile image
jakebman

This was originally in perldoc. I think it applies very generally if you take out the Perl-specific vocabulary:

(Almost any for or foreach construct) will get very confused if you add or remove elements to (what you're looping over) within the loop. So don't do that.

Collapse
 
chuckwood profile image
Charles Wood

I like to think of it as forEach not returning a value at all, whether or not that's true. That helps to remind me that it's purely for side effects.

Collapse
 
apixelvisuals profile image
APixel Visuals

I usually refer to docs when I forget how a specific function works, or if I need to explore what certain objects can do.

Collapse
 
dbilovd profile image
David Lartey

This makes me appreciate even more the work a community puts into getting very good documentations in place. Eg: Vue.js, Laravel, etc.

Collapse
 
ben profile image
Ben Halpern

I thought this tweet was pretty on-point

Collapse
 
link2twenty profile image
Andrew Bone

That tweet somes up my life so far 😁