Lodash _.matches Deep Comparison Explained
In the Lodash JavaScript library, the _.matches method
generates a predicate function designed to perform a deep partial
comparison between a target object and a predefined source object. This
article breaks down how this deep comparison mechanism operates, how it
evaluates nested properties and complex data types, and how its partial
matching logic differs from standard full-equality comparisons.
The Generated Predicate Function
When calling _.matches(source), Lodash produces a
function with the signature (object) => boolean. This
function accepts a single argument—the candidate object—and evaluates
whether the candidate conforms to the structure and values specified in
the initial source object. It acts as an accessor shorthand
commonly used in functional iterators like _.filter,
_.find, and _.some.
Deep Partial Comparison Mechanics
The comparison generated by _.matches is governed
internally by Lodash’s isMatch algorithm (specifically the
internal baseIsMatch function). The operation has two
distinct characteristics: it is partial, and it is
deep.
- Partial Equality: The function does not require the
target object to mirror the source object identically. Instead, the
candidate object must contain equivalent values for all property paths
present in the
source. Any extra properties present on the candidate object that were not defined insourceare ignored. - Deep Traversal: If a property in
sourceholds an object, array, or other complex data type,_.matchesrecursively inspects the nested structure rather than comparing object references via strict equality (===).
Type Handling and Equality Semantics
For value evaluation at each depth of the object tree, the generated
function utilizes deep equality semantics equivalent to
_.isEqual. This enables robust comparison across diverse
JavaScript types:
- Primitives: Compared using the SameValueZero
algorithm, correctly handling values like
NaN === NaNand treating+0and-0as equal. - Arrays: Matched by index and value. An array in
sourcerequires the target to have identical elements at the same indices. - Built-in Objects: Specialized deep checks are
applied to built-in objects.
Dateinstances are evaluated by their timestamps,RegExpobjects by their source patterns and flags, and wrappers (such asBooleanorNumberobjects) by their primitive values. - Maps and Sets: Evaluated based on entry and value equivalence.
Code Demonstration
const _ = require('lodash');
const criteria = {
user: {
role: 'admin',
settings: {
notifications: true
}
},
tags: ['active']
};
// Generates the deep partial comparison function
const isAdminWithNotifications = _.matches(criteria);
const candidateUser = {
id: 101,
user: {
role: 'admin',
username: 'johndoe',
settings: {
notifications: true,
theme: 'dark'
}
},
tags: ['active', 'verified'] // Note: arrays require exact match by index
};
// Returns false because tags[1] in the candidate does not exist in criteria array
console.log(isAdminWithNotifications(candidateUser)); Key Differences from Strict Equality
Unlike native comparison operators (===) or shallow
comparison utilities, the function created by _.matches
bypasses reference checks in favor of structural validation. It
traverses solely the paths defined in the reference object, confirming
that every leaf node satisfies deep equality while leaving non-specified
branches unexamined.