Lodash Array Intersection with Objects Edge Cases
Intersecting arrays of objects in Lodash is a common operation that
frequently introduces subtle bugs if developers rely on default
behaviors. While Lodash provides methods like
_.intersection, _.intersectionBy, and
_.intersectionWith, comparing non-primitive types
introduces complexities surrounding reference equality, deep structural
equivalence, property path collisions, duplicate retention, and
performance trade-offs. This article covers the specific edge cases
encountered when performing object array intersections using Lodash and
how to handle them correctly.
Reference Equality
Failures in _.intersection
The default _.intersection method uses the
SameValueZero comparison algorithm. Because JavaScript
objects are compared by reference rather than by value, two distinct
objects with identical structures and properties are not considered
equal.
const array1 = [{ id: 1 }];
const array2 = [{ id: 1 }];
_.intersection(array1, array2);
// Returns: []The method only matches elements if they point to the exact same
memory reference. To compare objects by structural value, developers
must use _.intersectionWith alongside a comparator like
_.isEqual.
Unintended
Merging from Missing Keys in _.intersectionBy
When using _.intersectionBy with an iteratee shorthand
(such as a string property path), objects lacking that property evaluate
to undefined. If multiple objects in either array lack the
target property, Lodash treats their keys as identical.
const listA = [{ id: 1 }, { name: 'Alpha' }];
const listB = [{ id: 2 }, { name: 'Beta' }];
_.intersectionBy(listA, listB, 'id');
// Returns: [{ name: 'Alpha' }]In this scenario, both { name: 'Alpha' } and
{ name: 'Beta' } yield undefined for the
'id' property. Because
undefined === undefined, Lodash treats them as matching
elements and retains the item from the first array.
First-Occurrence Bias and Property Discarding
When intersections match objects based on a subset of properties
(such as using _.intersectionBy with an id),
the resulting array only includes the instance found in the first array.
Any unique metadata stored exclusively on the matching object in the
second array is omitted.
const currentUsers = [{ id: 10, role: 'admin' }];
const incomingUsers = [{ id: 10, role: 'editor', lastLogin: '2025-01-01' }];
const result = _.intersectionBy(currentUsers, incomingUsers, 'id');
// Returns: [{ id: 10, role: 'admin' }]If the intention is to merge matching objects rather than merely
filter the first array, _.intersection variants are
insufficient on their own and require a subsequent merge strategy.
Automatic Deduplication
Lodash intersection methods inherently produce unique arrays. If the first array contains duplicate objects that match criteria in subsequent arrays, only the first occurrence is kept in the output.
const primary = [{ id: 1 }, { id: 1 }];
const secondary = [{ id: 1 }];
_.intersectionBy(primary, secondary, 'id');
// Returns: [{ id: 1 }] (length of 1, not 2)If the business logic requires preserving duplicate instances from
the source collection, standard array filtering via
Array.prototype.filter should be used instead of Lodash
intersection utilities.
Mutation of Shared References
Lodash returns a new array containing references to the original objects; it does not clone matched objects. Modifying an object in the intersected output directly mutates the object in the source collections.
const original = [{ id: 1, status: 'pending' }];
const filterList = [{ id: 1 }];
const matched = _.intersectionBy(original, filterList, 'id');
matched[0].status = 'complete';
console.log(original[0].status);
// Outputs: 'complete'Deep cloning must be implemented explicitly if the intersected results need to be manipulated independently from the source data.
Performance
Degradation with _.intersectionWith
While _.intersectionBy typically maps elements to a
Set-like lookup for linear \(O(n + m)\)
complexity, _.intersectionWith compares each element across
arrays using the custom comparator function. Using expensive comparators
like _.isEqual across large arrays leads to \(O(n \times m)\) computational complexity,
which can cause significant performance bottlenecks when processing
large datasets or deeply nested object structures.