DEV Community

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

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

msarit profile image Arit Amana ・2 min read

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?

Discussion (27)

pic
Editor guide
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
msarit profile image
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
msarit profile image
Arit Amana Author

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
msarit profile image
Arit Amana Author

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
Jermaine Easterling

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 😁