Skip to content

perf: use Set for O(1) lookups in uniq (#129)#130

Merged
yokuze merged 1 commit intomasterfrom
jthomerson/uniq-perf
Mar 26, 2026
Merged

perf: use Set for O(1) lookups in uniq (#129)#130
yokuze merged 1 commit intomasterfrom
jthomerson/uniq-perf

Conversation

@jthomerson
Copy link
Member

Summary

  • Fixes uniq uses O(n²) algorithm for unsorted arrays #129uniq used indexOf for dedup, making it O(n²)
  • Replaces with Set.has/Set.add in both standardUniq and
    iterateeUniq for O(n) amortized performance
  • At 40k elements, runtime drops from ~1.2s to under 50ms

Compatibility

This introduces a runtime dependency on Set (ES2015+). The
compiled output emits new Set() directly with no polyfill.
This is safe because the shared tsconfig already targets
es2019Set was already assumed available by every other
feature in the compiled output.

Test plan

  • Existing uniq tests pass (sorted, unsorted, iteratee)
  • New perf tests assert 40k elements complete in <50ms
  • Perf tests fail on the old implementation (~1.2s),
    pass on the new one

🤖 Generated with Claude Code

@jthomerson jthomerson requested a review from yokuze March 24, 2026 21:23
Copy link
Contributor

@yokuze yokuze left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jthomerson This works as-is, but I offered a couple of alternative implementations that are presumably a little faster and less code, if you'd like.

The perf tests were run in Chrome and since you may care most about Node.js, they could use a sanity check in Node.

@yokuze
Copy link
Contributor

yokuze commented Mar 25, 2026

Also, built-in Set is widely supported in browsers so we don't need to worry about the polyfill situation mentioned in the description: https://caniuse.com/mdn-javascript_builtins_set

Both `standardUniq` and `iterateeUniq` used `indexOf` to check
for duplicates, which is O(n) per element — making the overall
algorithm O(n²). At 40k elements this takes over a second.

`Set.has` uses SameValueZero equality, which matches `indexOf`
behavior for all practical values (the only difference is NaN,
which is now treated as equal to itself — arguably a bug fix).

This introduces a runtime dependency on `Set` (ES2015+). The
compiled output emits `new Set()` directly with no polyfill,
since the shared tsconfig already targets `es2019` — meaning
`Set` was already assumed available by every other feature in
the compiled output (arrow functions, `const`, etc.).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jthomerson jthomerson force-pushed the jthomerson/uniq-perf branch from 7812ee6 to 7067397 Compare March 26, 2026 03:57
@jthomerson jthomerson requested a review from yokuze March 26, 2026 03:57
Copy link
Contributor

@yokuze yokuze left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right. I missed that. LGTM

@yokuze yokuze merged commit 7ea221e into master Mar 26, 2026
14 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

uniq uses O(n²) algorithm for unsorted arrays

2 participants