Lodash isMatch Logic for Nested Arrays of Objects

This article explains the internal comparison algorithm executed by Lodash's _.isMatch when evaluating deeply nested arrays of objects. It details how Lodash propagates partial comparison flags, performs existential subset searches rather than strict index matching across arrays, handles recursive object matching, and prevents infinite recursion using internal stack tracking.

The Comparison Bitmask and Flag Propagation

When _.isMatch(object, source) is invoked, Lodash delegates the operation to internal functions—primarily baseIsMatch and baseIsEqual. The evaluation applies a bitwise flag known internally as COMPARE_PARTIAL_FLAG (value 1).

This flag enforces partial matching throughout the traversal. Unlike strict equality checks (such as _.isEqual), which require identical structure, key counts, and array lengths, the partial flag instructs Lodash to only verify that values defined in source exist within object. This flag persists recursively down through every level of nested objects and arrays.

Object-Level Comparison Logic

For each object encountered:

  1. Key Inspection: Lodash extracts all own enumerable string-keyed properties from the source object using keys(source).
  2. Existence Verification: It checks if each key in source is present in the target object.
  3. Deep Value Delegation: If a key exists in both, Lodash recursively evaluates the two values with baseIsEqual, passing down the COMPARE_PARTIAL_FLAG. If any key in source is missing or fails the recursive match, the routine immediately short-circuits and returns false.

Array-Level Comparison Logic (Existential Subset Matching)

When the algorithm encounters arrays inside nested objects, the behavior diverges significantly from standard equality:

  1. No Strict Index Binding: Lodash does not require elements in the source array to appear at the exact same index in the target array.
  2. Subset Validation: The algorithm checks whether every item in the source array matches at least one item in the target array. The target array may contain additional elements not present in the source array without causing the comparison to fail.
  3. Duplicate Prevention: When evaluating whether each source item has an equivalent in the target, Lodash tracks matched indices in the target array. An individual element in the target array cannot be used to satisfy multiple distinct elements in the source array unless duplicates exist in the target.

Evaluating Nested Arrays of Objects

When arrays contain nested objects, these two mechanisms operate in tandem recursively:

  1. Outer Array Step: Lodash iterates through the elements of the source array.
  2. Candidate Search: For each object in the source array, it scans the target array to find a candidate object that satisfies a partial match.
  3. Inner Object Step: The candidate object in the target array is checked to see if it contains all properties of the source object. If the source object itself contains another nested array, the existential array-matching algorithm runs again for that nested layer.
  4. Backtracking and Matching: If a target object partially matches the source object, that target item is flagged as consumed for this comparison branch. If it fails, the search moves to the next item in the target array.
  5. Resolution: If every object in the source array successfully finds a partially matching, unconsumed counterpart in the target array, the array comparison resolves to true. If any source item finds no corresponding target object, it resolves to false.

Circular References and Short-Circuiting

To maintain performance and prevent call-stack overflows during deep traversals, Lodash uses an internal Stack cache. As objects and arrays are traversed, their references are stored in this stack. If a circular reference is encountered, Lodash checks whether the pair is already currently being evaluated, preventing infinite recursion. Additionally, comparison functions evaluate primitives and mismatches eagerly, short-circuiting to false at the earliest point of divergence.