DEV Community

Barney
Barney

Posted on

can't use .filter() after .forEach()

Hi!

So I came across this problem.

I have an array of N elements. I'd like to run every element against a function.

so I would go:

myArray.forEach(item=>process(item)
Enter fullscreen mode Exit fullscreen mode

Now I want to filter some of them and call another function on the rest of them.

   .forEach(item=>process(item)
   .filter( somelogic )
   .forEach( item=>postProcessSome(item) );
Enter fullscreen mode Exit fullscreen mode

But that's not valid code. forEach returns undefined so I can't call filter. I can use map instead of forEach but that way I can't use one liners.

What's an elegant way to do this? For example in Java I could use .peek that acts like a foreach but returns the value.

Thanks for help.

Top comments (9)

Collapse
 
vonheikemen profile image
Heiker

.map is the only option here. If the problem is process return value you can do this.

myArr.map(item => (process(item), item))
  .filter( somelogic )
  .map(item => postProcessSome(item));

If process doesn't mutate item that should be fine. You could even make a helper function.

const tap = (fn) => (value) => (fn(value), value);

array.map(tap(item => process(item)))
// ... the rest
Collapse
 
lionelrowe profile image
lionel-rowe

Comma operator for implied return from arrow function is kinda gnarly... though I guess that's a matter of personal style preference.

Collapse
 
lagsurfer profile image
Barney

TIL comma operator! Thanks!

Collapse
 
lionelrowe profile image
lionel-rowe • Edited

What's wrong with this? Easy to reason about and the same total number of lines, though line count is a bit of an artificial metric - better to prioritize readability.

myArr.forEach(item => {
  process(item)
  if (someLogic(item)) postProcess(item)
})
Collapse
 
lagsurfer profile image
Barney

For readability this is definitely the winner and it only iterates over the array once.

Collapse
 
vishnumohanrk profile image
Vishnu

Use .map()

myArr.map(item => process(item))
  .filter( somelogic )
  .map(item => postProcessSome(item));
Collapse
 
lionelrowe profile image
lionel-rowe

This won't work unless process(item) returns item.

Collapse
 
mdor profile image
Marco Antonio Dominguez

You can even use a simple reduce

myArray
  . reduce((acc, item) => {
    // Filter if met some condition, and add the proccesed item
    if (somelogic)  acc.push(process.item)
   return acc;
  }, [])
 .forEach(item => /*anything */ )

/* Compact should be */
myArray
  . reduce(
    (acc, item) => (somelogic && acc.push(process(item)), acc), []
  )
  .forEach(item => /*anything */ )

Collapse
 
lagsurfer profile image
Barney

Nice! Very cool utilization of reduce I like it.