Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent spread ...obj inside a arr.reduce() #18

Open
styfle opened this issue May 16, 2022 · 6 comments
Open

Prevent spread ...obj inside a arr.reduce() #18

styfle opened this issue May 16, 2022 · 6 comments
Labels
enhancement New feature or request eslint

Comments

@styfle
Copy link
Member

styfle commented May 16, 2022

I've seen this pattern used several times where the developer is using spread inside a reduce causing a O(N^2) algorithm when avoiding the reduce would lead to O(N).

Example usage we want to avoid:

O(N^2)

  const originalFiles = {}; // Imagine 100k files

  const remappedFiles = Object.keys(originalFiles).reduce(
    (mappedFiles, file) => ({
      ...mappedFiles,
      [`_next/${file}`]: originalFiles[file]
    }),
    {}
  );

Could we write a lint rule to prevent this pattern and guide the developer toward the O(N) solution?

Example usage we should encourage:

O(N)

  const originalFiles = {}; // Imagine 100k files
  const remappedFiles = {};
  for (const file of Object.keys(originalFiles)) {
    remappedFiles[`_next/${file}`] = originalFiles[file];
  }
@mrmckeb mrmckeb added eslint enhancement New feature or request labels May 17, 2022
@mrmckeb
Copy link
Contributor

mrmckeb commented Jun 10, 2022

This is quite interesting, have you seen any lint rules around this that we could turn on?

@mrmckeb
Copy link
Contributor

mrmckeb commented Sep 21, 2022

@styfle did you have any thoughts around how we might introduce a rule like this?

Obviously this would need some discussion, as a lot of JS engineers like functional programming.

@styfle
Copy link
Member Author

styfle commented Sep 21, 2022

Obviously this would need some discussion, as a lot of JS engineers like functional programming.

I think there is little to discuss when we consider performance.

In fact, @pvdz just found a case where changing the spread in the loop brought the build time down from 18 min to 4 minutes.

Also see The reduce ({...spread}) anti-pattern blog post.

@styfle
Copy link
Member Author

styfle commented Sep 21, 2022

did you have any thoughts around how we might introduce a rule like this?

I assume we need to write a eslint plugin. Something like this:

https://github.com/mkosir/eslint-plugin-no-array-reduce/blob/63b7792d1bbf3f8d3578fdb6fbde5982555f12c0/src/rules/disallowArrayMethod.ts#L11-L24

Or we could disable .reduce() completely with no-array-reduce, but that might upset some engineers as you mentioned.

@AlexErrant
Copy link

I googled "javascript spread reduce eslint" after reading an article on Tanstack Table and arrived here. I think a lot of devs (including myself) don't think about the implications of .... I'm not smart enough to help write a rule; just here to express interest :)

@Conaclos
Copy link

Conaclos commented Dec 4, 2023

We added this rule to Biome several months ago. Any feedbacks are welcomed :)

Boshen pushed a commit to oxc-project/oxc that referenced this issue Dec 5, 2023
Creates a new nursery-level rule, `no-reduce-spread`, that prevents
spreading accumulator objects/arrays in `Array.prototype.reduce`.

The Tl;Dr of why this should be avoided is because it drastically
increases time/memory complexity in reductions. In general, it turns
potentially `O(1)`/`O(n)` memory operations into `O(n)`/`O(n^2)`, and
`O(n)` time into `O(n^2)`. For more details, refer to [this helpful blog
post](https://prateeksurana.me/blog/why-using-object-spread-with-reduce-bad-idea/).

Note that this rule doesn't exist in ESLint's default rule set or any
other ESLint plugin, although it looks like [there's been some interest
in it in the past](vercel/style-guide#18).
Since there is no reference implementation to compare against, and thus
this could potentially have bugs, I've decided to make this a
nursery-level rule until we're sure it's stable and useful.

---

Edit: 

- [ ] Sync with Biome -
https://biomejs.dev/linter/rules/no-accumulating-spread/

---------

Co-authored-by: Cameron Clark <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request eslint
Projects
None yet
Development

No branches or pull requests

4 participants