Much needed filterMap in JavaScript

Akash Kava on December 21, 2018

Most often we perform filter followed by map and it turns out that combining both in one function would certainly avoid multiple iteration. Ar... [Read Full]
markdown guide
 

The same exact logic but written with reduce instead of a for loop.

Array.prototype.filterMap = function(filter) {
  return this.reduce((acc, x, i) => {
    const item = filter(x, i, this);
    if (item !== undefined) {
      acc.push(item)
    }
    return acc
  }, [])
}

more info: Map, Filter, Reduce vs For Loops (syntax)

Also while this function is very cleaver, it is now considered an anti-pattern to modify the prototype of built in objects.

We want to avoid any "smoosh gate" fiasco in the future.

 

Well we can have an independent function with array being first parameter. I really don’t care about anti pattern because extending prototype is the best feature of JS. Otherwise everything is possible in other languages

 

Well we can have an independent function with array being first parameter.

That is what I would recommend

I really don’t care

But see, you should. Something seemingly harmless as extending a prototype has cause much difficulty in the JavaScript community due to name collisions.

An older but popular library extended Array to include flatten. The problem was this version conflicted with the implementation proposed by the TC39. Because the TC39 could not use flatten without breaking the internet, the dumb name smoosh was recommended. github.com/tc39/proposal-flatMap/p...

This has happened multiple times.

Today the TC39 is currently arguing over a name for a global context object. Because node has called their global object global, this name cannot be used without breaking the internet again. So they are currently proposing the horrible name of globalThis github.com/tc39/proposal-global/is...

While your code may have little impact now. It could have very large effects in the future.

So you should definitely care.

Cheers!

 

If I were to implement something like filterMap, I would pass in the predicate and the transformation as separate functions. However, you can use reduce for what you're doing, no need to implement a new method on Array.

const emails = customers.reduce(
  (prev, c) => c.active
    ? [ ...prev, c ]
    : prev,
  []
)
 
 

Yeah, sorry, been using array destructuring too much lately. If it's speed we're going after we can't create a new array at each iteration step.

const emails = customers.reduce(
  (prev, c) => {
    if (c.active) prev.push(c)
    return prev
  },
  []
)

This way reduce can be used as map as well, nice trick but I still prefer filterMap for its smaller syntax.

 
Array.prototype.filterMap = function(filter) {
   const r = [];
   for(let i=0; i<this.length; i++) {
      const item = filter(this[i], i, this);
      if (item !== undefined) {
         r.push(item);
      }
   }
   return item; // should be `return r`. right.?
};```

 
 

There is the flatMap method that does exactly that filter + map :)
this method not only lets you dynamically filter the array, but it also gives the ability to add elements to the array dynamically.

developer.mozilla.org/en-US/docs/W...

// example
someArray.flatMap((element) => {
  if(element.isNotNeeded){
    return [];
  }

  return [ element.someProperty ];
})
 

Ideally, filterMap shouldn't restrict me from mapping my elements into anything I could map it to. Including undefined.

[1, 2, 3, 4].filterMap(n => n % 2, n => `${n} is even`)

This would be a better Api, really similar to a separate filter and map, but with one loop inside.

code of conduct - report abuse