Skip to content

feat: add matches and isMatch functions - #386

Merged
ppeeou merged 8 commits into
mainfrom
feat/matches
Jun 17, 2026
Merged

ppeeou merged 8 commits into
mainfrom
feat/matches

Conversation

@ppeeou

@ppeeou ppeeou commented Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #380

Based on the original PR #383 (@boy672820), with an added bug fix for
top-level non-object patterns in matches and a hot-path performance
improvement.

Summary

  • Add matches — creates a predicate that checks whether an input matches
    all properties in a pattern
  • Add isMatch — performs a partial deep comparison between two values

Features

  • Deep comparison for nested objects and arrays
  • Support for Date, RegExp, Map, Set (Set uses bipartite matching, so it is
    order-independent)
  • Works with filter, find, some, and every

Additional changes (on top of the original PR)

  • Bug fix: matches only iterated the pattern's top-level entries, so a
    top-level Date/RegExp/Map/Set/Array/primitive pattern (which has no
    enumerable string keys) matched every input vacuously.
    e.g. previously matches(new Date("2024"))(new Date("2025")) === true →
    now false.
  • Special-type / primitive patterns now delegate value comparison to
    isMatch, while the common plain-object pattern uses a fast path that
    hoists Object.keys out of the predicate (computed once at predicate
    creation instead of on every call) — ~2.5x faster than always delegating
    on a 100k-item filter.
  • Added 8 regression tests (top-level non-object patterns, nil-pattern
    contract, consistency with isMatch).

- SameValueZero for primitives
- Type guards for Date/RegExp/Map/Set
- DFS for Set matching
- Array partial match
…ymbolKeys

- Use DeepPartial<T> & RejectSymbolKeys<T> for partial matching and Symbol rejection
- Add DeepWiden<T> to widen literals to base types
@ppeeou ppeeou self-assigned this Jun 17, 2026
matches() iterated only the pattern's top-level entries, so a top-level
Date/RegExp/Map/Set/Array/primitive pattern (which has no enumerable
string keys) matched every input vacuously. Delegate those to isMatch so
they are compared by value, while keeping a fast path for the common
plain-object pattern that hoists Object.keys out of the predicate hot
path (~2.5x faster than always delegating on a 100k-item filter).
@ppeeou
ppeeou merged commit 8e25950 into main Jun 17, 2026
2 checks passed
@ppeeou
ppeeou deleted the feat/matches branch June 17, 2026 02:40
ppeeou added a commit that referenced this pull request Jun 17, 2026
* feat: add isMatch function

* feat: add matches function

* feat: add matches and isMatch spec description

* fix: correct isMatch for NaN, types, Set/Array

- SameValueZero for primitives
- Type guards for Date/RegExp/Map/Set
- DFS for Set matching
- Array partial match

* fix: enhance pattern type safety with DeepPartial, DeepWiden, RejectSymbolKeys

- Use DeepPartial<T> & RejectSymbolKeys<T> for partial matching and Symbol rejection
- Add DeepWiden<T> to widen literals to base types

* docs: add Symbol key handling details and new examples to matches and isMatch docs

* docs: align isMatch array matching descriptions

* fix: compare top-level non-object patterns in matches by value

matches() iterated only the pattern's top-level entries, so a top-level
Date/RegExp/Map/Set/Array/primitive pattern (which has no enumerable
string keys) matched every input vacuously. Delegate those to isMatch so
they are compared by value, while keeping a fast path for the common
plain-object pattern that hoists Object.keys out of the predicate hot
path (~2.5x faster than always delegating on a 100k-item filter).

Co-authored-by: boy672820 <boy672820@gmail.com>
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.

feat: add matches function for object pattern matching

2 participants