Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I avoid using object spread in reduce?

Given the following example:

// option 1
items.reduce((values, item) => ({
     ...values,
     [item.id]: item.name
}), {})


// option 2
items.reduce((values, item) => {
    values[item.id] = item.name;
    return values;
}, {});

Is there a best practice pro or contra using object spread syntax in this case?

like image 726
guy mograbi Avatar asked Mar 04 '23 02:03

guy mograbi


2 Answers

Option 2 is clearly preferable for performance reasons:

  • Option 1 runs in O(n²) time, since the spread syntax copies O(n) properties on each iteration. Option 2 runs in O(n) time.
  • Option 1 creates O(n²) garbage, since it creates a garbage object of size O(n) on each iteration. Option 2 creates no garbage.

That said, in most cases you should just write this with a plain old for loop:

let result = {};
for(let item of items) {
    result[item.id] = item.name;
}

It is not bad to use for loops, and the code is even more readable than the two options in the question. Option 2 may seem like it is more in the functional programming style, but if you are using mutation to achieve your desired result then you are not really doing functional programming.

See this article for a more in-depth discussion of why Option 1 is an antipattern.

like image 92
kaya3 Avatar answered Mar 08 '23 00:03

kaya3


In the first code, you're creating a new object for every iteration of .reduce. In certain engines, this may be slightly less efficient than your second code, which only creates a single object. (That said, efficiency rarely matters much; code clarity is much more important in most situations).

But, for this situation, there's an even more suitable method to use when creating an object from an array, which avoids the slightly clunky syntax of reduce:

const output = Object.fromEntries(
  items.map(item => [item.id, item])
);

const items = [
  { id: 5, val: 5 },
  { id: 10, val: 10 },
  { id: 15, val: 15 },
];
const output = Object.fromEntries(
  items.map(item => [item.id, item])
);
console.log(output);

That said, keep in mind that Object.fromEntries is a relatively new feature, so if this is meant for a public-facing website, make sure to include a polyfill.

like image 26
CertainPerformance Avatar answered Mar 08 '23 01:03

CertainPerformance