paint-brush
Solving Performance Issues with .filter() and .map() Used in Conjunctionby@MercifulTyrant
1,704 reads
1,704 reads

Solving Performance Issues with .filter() and .map() Used in Conjunction

by Deepak ManiFebruary 17th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Modern extensions make it easier to write code that performs badly. Map() and filter() are used in conjunction to death galore in code nowadays because “it’s so easy” But does blindly using these methods cause performance issues in your code? Let us dig in and find out. The problem is that the original array (or its filtered result) is being looped twice. Figure out how to reduce (ahem… hint, wink wink) the # of loops and the issue is resolved.

Coin Mentioned

Mention Thumbnail
featured image - Solving Performance Issues with .filter() and .map() Used in Conjunction
Deepak Mani HackerNoon profile picture

Post their introduction, .map() and .filter() are used in conjunction to death galore in code nowadays because “it’s so easy”. But does blindly using these methods cause performance issues in your code? Let us dig in and find out. I am not going to get into the details of how these functions work because there is TONS of material available for the same.

[Theory Alert]

Those familiar with the Big ‘O’ notation are probably aware that .map() and .filter() execute at O(n) where n is the number of elements of the Array in question.

Therefore, chaining filter().map() implies that the running time for this execution chain is O(n) + O(n) at its worst case.

Damn, these modern extensions make it easier to write code that performs badly.

So, what do we do?

Well, it is pretty clear that the issue here is that the original array (or its filtered result) is being looped twice. Figure out how to reduce (ahem… hint, wink wink) the # of loops and the issue is resolved.

If you are now asking, how hard that can be and if you are going to end up writing a lot of boiler plate code, fear not.

.reduce() to the rescue

The reduce method executes a reducer function (that you provide) on each element of the array, resulting in a single output value. The interesting part however, is that this single output value can also be an array.

Therefore, it becomes easy for us to implement a function that loops through the original array and performs both the filter() and the map() actions at one shot. Preferably, you would filter before you map, for performance gains (hint: less # of items to go through), unless there is a reason to do otherwise.

So, if we write some code for the same, it would look as follows.

function filterAndMap(arr, filterCallback, mapCallback) {
     let reduced = arr.reduce(function(filteredAndMapped, item) {
     if (filterCallback(item)) {
         let mappedItem = mapCallback(item);
         filteredAndMapped.push(mappedItem);
     }
     return filteredAndMapped;
   }, []);
   return reduced;
}

Now, you have a function that expects 3 arguments. The array you want to process, the callback for testing the filter condition and the callback to transform the result into whatever you want. Reading the code, you can see that the Array is iterated through just once, thus getting us the required performance gain.

Now to make this an extension method for the Array object. This can be easily done by crafting a prototype method for Array.

Et Voila

To make the above function a prototype method, we have to do a few things. First of all, we have to handle the case of “this”, which can mean different things in different context — think nodejs, browser based javascript. So, our prototype method will look something like below.

Array.prototype.filterMap = function(filterCallback, mapCallback) {
  let obj = Object(this);
  if (obj.length === 0) return null;
if (typeof filterCallback === "undefined") throw;
  if (typeof mapCallback === "undefined") throw;
let reduced = this.reduce(function(filteredAndMapped, item) {
    if (filterCallback(item)) {
      let mappedItem = mapCallback(item);
      filteredAndMapped.push(mappedItem);
    }
    return filteredAndMapped;
  }, []);
return reduced;
};

That’s all Folks. Presenting Array.prototype.filterMap()

And that is how we have a simple code snippet that allows us to use both .filter() and .map() together and not lose out on code performance.

Cheers and stay tuned for more snippets like these. I will update this article once I create a npm module for .filterMap() and publish it, for those who do not want to copy and paste code <wink wink>.

Previously published at https://medium.com/@deepak.mani.0211/filtermap-in-javascript-solving-performance-issues-with-filter-and-map-used-in-conjunction-f814da71faf