Skip to content

Conversation

@Yeom-JinHo
Copy link
Contributor

Closed #1512

Description

This PR updates the uniqBy utility to support both selector functions and property keys, and aligns its documentation and tests with the new API.
The goal is to make uniqBy easier to use in common cases (e.g. deduplicating by 'id' or 'category') while keeping full backward compatibility with the existing function-based usage.


Changes

  • Updated the uniqBy function signature to accept a selector parameter of type ((item: T) => U) | keyof T instead of a mapper function only.
  • Internally, uniqBy now uses Map<U | T[keyof T], T> as the backing store so it can handle keys produced either by a selector function (U) or by a property lookup (T[keyof T]).
  • Adjusted the implementation to derive the key via:
    • selector(item) when selector is a function, or
    • item[selector] when selector is a property key.
  • Renamed the parameter conceptually from “mapper” to “selector” in JSDoc and docs to better reflect that it can be either a function or a property key.
  • Added tests to cover uniqBy usage with property keys (e.g. uniqBy(items, 'id'), uniqBy(products, 'category')), in addition to the existing selector-function tests.
  • Updated the uniqBy MDX docs:
    • Replaced mapper with selector in the API description and examples.
    • Documented that the selector can be a function or a property key.
    • Added examples showing key-based usage (e.g. 'age', 'category').

Motivation

uniqBy already encourages a “pick a value and deduplicate by it” pattern, but requiring an arrow function for simple cases (e.g. uniqBy(users, u => u.id)) can be unnecessarily verbose. Since sortBy in the same toolkit already supports both property keys and selector functions, extending uniqBy in a similar way improves API consistency and ergonomics.

This change makes common usage like uniqBy(users, 'id') or uniqBy(products, 'category') more readable, without forcing any changes on existing call sites.


Breaking Changes

There are no breaking changes in this PR.

  • Existing usages like uniqBy(array, x => x.someField) remain valid and type-compatible.
  • The parameter has been conceptually renamed from mapper to selector, but the function still accepts any previous function-based selector.
  • Runtime behavior is unchanged for all existing call sites; the new behavior only extends the accepted input shape.

Copilot AI review requested due to automatic review settings November 24, 2025 11:17
@vercel
Copy link

vercel bot commented Nov 24, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
es-toolkit Ready Ready Preview Comment Nov 24, 2025 11:27am

Copilot finished reviewing on behalf of Yeom-JinHo November 24, 2025 11:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the uniqBy utility function to accept either a selector function or a property key (like 'id' or 'category'), improving ergonomics and consistency with similar functions like sortBy and orderBy. The change maintains full backward compatibility while making common use cases more concise.

Key Changes:

  • Extended uniqBy signature to accept ((item: T) => U) | keyof T instead of just a function
  • Updated implementation to handle both selector functions and property key lookups
  • Added comprehensive test coverage for property key usage scenarios

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/array/uniqBy.ts Extended function signature and implementation to support property keys alongside selector functions
src/array/uniqBy.spec.ts Added tests for property key selector usage with numeric and string fields, plus edge cases
docs/reference/array/uniqBy.md Updated English documentation with new API signature and property key examples
docs/zh_hans/reference/array/uniqBy.md Updated Chinese documentation to reflect the new selector parameter and usage patterns
docs/ko/reference/array/uniqBy.md Updated Korean documentation with examples of both selector functions and property keys
docs/ja/reference/array/uniqBy.md Updated Japanese documentation to describe both selector function and property key usage

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

*/
export function uniqBy<T, U>(arr: readonly T[], mapper: (item: T) => U): T[] {
const map = new Map<U, T>();
export function uniqBy<T, U>(arr: readonly T[], selector: ((item: T) => U) | keyof T): T[] {
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The generic type T should be constrained with extends object for type safety when accepting property keys. Similar functions in the codebase (sortBy and orderBy) use this constraint. Without it, callers could pass primitive types with property keys, leading to runtime errors. Change to: export function uniqBy<T extends object, U>(arr: readonly T[], selector: ((item: T) => U) | keyof T): T[]

Suggested change
export function uniqBy<T, U>(arr: readonly T[], selector: ((item: T) => U) | keyof T): T[] {
export function uniqBy<T extends object, U>(arr: readonly T[], selector: ((item: T) => U) | keyof T): T[] {

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

However, constraining T with extends object here would introduce a breaking change. uniqBy currently supports primitive arrays (e.g. uniqBy([1.2, 1.5, 2.3], Math.floor)), and this pattern is already documented and tested. If we change the signature to T extends object, those existing usages would no longer type-check.

Because of that, I’d prefer not to add the extends object constraint on T for uniqBy in this PR. I’d like to keep primitive use cases supported and avoid turning this into a breaking change.

Comment on lines +36 to +37
export function uniqBy<T, U>(arr: readonly T[], selector: ((item: T) => U) | keyof T): T[] {
const map = new Map<U | T[keyof T], T>();
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The Map key type U | T[keyof T] is overly broad and doesn't accurately represent the actual key types. When selector is a property key K, the key should be T[K], not T[keyof T]. Consider using function overloads to provide more precise typing:\ntypescript\nexport function uniqBy<T extends object, K extends keyof T>(arr: readonly T[], selector: K): T[];\nexport function uniqBy<T extends object, U>(arr: readonly T[], selector: (item: T) => U): T[];\nexport function uniqBy<T extends object, U>(arr: readonly T[], selector: ((item: T) => U) | keyof T): T[] {\n // implementation\n}\n

Suggested change
export function uniqBy<T, U>(arr: readonly T[], selector: ((item: T) => U) | keyof T): T[] {
const map = new Map<U | T[keyof T], T>();
export function uniqBy<T extends object, K extends keyof T>(arr: readonly T[], selector: K): T[];
export function uniqBy<T, U>(arr: readonly T[], selector: (item: T) => U): T[];
export function uniqBy<T, U>(arr: readonly T[], selector: ((item: T) => U) | keyof T): T[] {
const map = new Map<any, T>();

Copilot uses AI. Check for mistakes.
@codecov-commenter
Copy link

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.97%. Comparing base (335011b) to head (8fb77de).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #1534   +/-   ##
=======================================
  Coverage   99.97%   99.97%           
=======================================
  Files         474      474           
  Lines        4492     4492           
  Branches     1313     1314    +1     
=======================================
  Hits         4491     4491           
  Misses          1        1           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Yeom-JinHo Yeom-JinHo requested a review from Copilot November 24, 2025 12:28
Copilot finished reviewing on behalf of Yeom-JinHo November 24, 2025 12:30
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@bertyhell bertyhell left a comment

Choose a reason for hiding this comment

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

lgtm

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.

Inconsistent api between sortBy and uniqBy

3 participants