Beware the Reduce / Spread Operator Combination!
Nick Scialli
August 24, 2019
This is a short post about a popular pattern that has been emerging recently: using the Array reduce
method and spread operator (...
) together. Let’s look at a quick example where we use them together to swap the keys and values of an object.
const obj = { a: 1, b: 2, c: 3 };
const flipped = Object.entries(obj).reduce(
(all, [key, val]) => ({
...all,
[val]: key,
}),
{}
);
console.log(flipped);
// { '1': 'a', '2': 'b', '3': 'c' }
Pretty cool, right?
The Problem: Using Reduce and Spread Together Can Be Pretty Inefficient for Large Arrays
If you study the example above, you’ll realize that we’re creating a shallow copy of our all
object each time we execute the function being passed to the reduce
method. This may not be a big deal for small arrays, but can quickly become a performance bottleneck. Let’s create a really large array and test how our reduce/spread combination performs.
const largeArr = new Array(100000).fill(1).map((_, i) => i);
// [0, 1, 2, 3, ..., 99999]
console.time('try 1');
const obj1 = largeArr.reduce(
(all, el) => ({
...all,
[el]: el,
}),
{}
);
console.timeEnd('try 1');
// try 1: 20985.787ms
Yikes, that took 21 seconds! So what are we to do?
Plain Old Loops to the Rescue
In a lot of cases, it turns out that a plain old for
loop or forEach
method will be sufficient for the task. In the following example, we accomplish the same task as the above code in only ~5 milliseconds.
console.time('try 2');
const obj2 = {};
for (let i = 0; i < largeArr.length; i++) {
obj2[largeArr[i]] = largeArr[i];
}
console.timeEnd('try 2');
// try 2: 4.620ms
Conclusion
Think hard before using reduce
and spread (...
) together! You’re probably fine if you’ll be working with small arrays, but can definitely take a performance hit when your arrays get large.
Nick Scialli is a senior UI engineer at Microsoft.