Javascript's Array class exposes quite a few methods (filter, map, reduce), which iterate through an array and call an iterator function to perform actions on the array. Chaining these methods allows you to write clean, easy to read code. But what does this convenience cost us in terms of performance, and is it worth it?


Danger Signage

Javascript is a 'functional' language. What this means is that functions are first class objects in Javascript, and as such they can be passed on as parameters to other functions. There are quite a few built-in methods provided by the Javascript standard library, which makes use of this fact, to enable us to write, clean, understandable, and easy to read code.

One such built-in class which makes extensive use of Javascript's functional nature, is the Array class. Arrays in Javascript, expose a number of instance methods, which:

  • accept a function as an argument,
  • iterate upon the array,
  • and call the function, passing along the array item as a parameter to the function.

The most popular of these are of course forEach, filter, map and reduce. Since these methods also return the Array instance as the return value of the method, they are often chained together, like this:

const tripExpenses = [{
    amount: 12.07,
    currency: 'USD',
    paid: true
}, {
    amount: 1.12,
    currency: 'USD',
    paid: true
}, {
    amount: 112.00,
    currency: 'INR',
    paid: false
}, {
    amount: 54.17,
    currency: 'USD',
    paid: true
}, {
    amount: 16.50,
    currency: 'USD',
    paid: true
}, {
    amount: 189.50,
    currency: 'INR',
    paid: false
}];

const totalPaidExpensesInINR = tripExpenses
    .filter(expense => expense.paid)
    .map(expense => {
        if(expense.currency == 'USD')
            return expense.amount * 70;
        else
            return expense.amount;
    })
    .reduce((amountA, amountB) => amountA + amountB);

In this example, we are calculating the total paid expenses, after converting them from USD to INR. To do this, we are:

  • filtering tripExpenses to extract only the paid expenses,
  • mapping the expense amount from the specified currency and converting it to INR, and
  • reduceing the INR amounts, to get the sum.

Currently, our tripExpenses array only has 6 items, so this is relatively fast, but what happens when we have to analyze the trip expenses for say an entire companies worth of employees for the entire financial year, and our tripExpenses array starts to have hundreds of thousands of elements?

Thanks to JSPerf, we can visualize this cost quite easily. So let's run a comparison test for the same code with tripExpenses having 10 elements, 10,000 elements, and 100,000 elements. Here's the result of the JSPerf comparison:

JSPerf 10 vs 10k vs 100k

The graph shows the number of operations per second, and higher is better. While I expected the 100,000 elements case to perform poorly, I really did not expect the 10,000 elements case to perform this poorly. Since it's not really visible on the chart, let's look at the numbers:

  • 10 Elements - 6,142,739 ops per second
  • 10,000 Elements - 2,199 ops per second
  • 100,000 Elements - 223 ops per second

Yikes, thats really bad! And while processing an array of 100,000 elements might not happen often, 10,000 elements is a very plausible use case, that I've seen regularly in multiple applications I've developed (mostly on the server side).


This shows us, that when we write – even what seems to be quite simple code – we really should be watching out for any performance issues that might crop up because of the way we write our code. If instead of chaining the filter, map and reduce methods together, we rewrite our code such that, all the work gets done in a single loop, we can gain significantly better performance.

let totalPaidExpensesInINR = 0;

for(let expense of tripExpenses){
    if(expense.paid){
        if(expense.currency == 'USD')
            totalPaidExpensesInINR += (expense.amount * 70);
        else
            totalPaidExpensesInINR += expense.amount;
    }
}

Let's run another JSPerf comparison to see how this performs against it's functional counterpart, in a 10,000 element test:

JSPerf functional vs for-of

As you can see, on Chrome (and by extension Node.JS), the functional example is a whopping 77% slower than the for-of example. On Firefox, the numbers are a lot closer, but the functional example is still 16% slower than the for-of example.


So why is the functional example so much slower than the for-of example? Well it's a combination of factors, but the primary factors that as a developer, we can control, from user land are:

  • Looping over the same array elements multiple times.
  • Overhead of function calls for each iteration in the functional example.

If you see the for-of example, you'll see that we only ever iterate through the tripExpenses array once, and we call no functions from within, instead performing our calculations inline.


Some developers may consider the for-of example to be less readable, and more difficult to understand than the functional example. For this particular example, I'd say both the examples are equally readable. However, in the case of the functional example the convenience of the method chaining, tends to hide the multiple iterations and function calls from the developer, thus making it easy for an inexperienced developer to write non-performant code.

I'm not saying that you should always avoid the functional way, I'm sure there are plenty of valid cases for which using the functional way and for chaining the methods, but a general rule of thumb to remember when it comes to performance, and iterating arrays in Javascript, is that if you're chaining methods which iterate through the entire array, you should probably stop and consider the performance impact, before going ahead.


I'd love to hear your opinion on things I've written in this article. Unfortunately, I've not yet found a nice lightweight comments widget to add on to my blog, so until then the only way I can take comments is via email. Do write to me at asleepysamurai at google's mail service.