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
 
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
 
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
 
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.