Lodash _.xor Behavior with Structural References

This article examines how Lodash’s _.xor function handles arrays containing structurally identical objects and references. By default, _.xor determines symmetric differences using shallow reference comparisons (SameValueZero), meaning that objects with identical keys and values will not be treated as duplicates unless they share the exact same memory reference. Understanding this distinction is critical for developers expecting deep equality checks during array transformations.

Reference Equality vs. Structural Equality

Lodash’s _.xor creates an array of unique values that is the symmetric difference of the provided arrays. It filters out elements that appear across multiple arrays. However, its internal uniqueness and difference operations rely on SameValueZero comparison rather than deep structural inspection.

When arrays contain non-primitive types—such as objects or nested arrays—_.xor evaluates whether the items point to the same location in memory.

Identical Structural Values with Different References

If two objects have identical properties and values but are instantiated separately, _.xor views them as distinct values. Consequently, both objects will appear in the output array.

const objA = { id: 1 };
const objB = { id: 1 };

const result = _.xor([objA], [objB]);
// result: [{ id: 1 }, { id: 1 }]

Because objA !== objB, neither object is canceled out during the symmetric difference calculation.

Shared Object References

When two arrays contain a reference to the exact same object in memory, _.xor recognizes them as identical. In this scenario, the shared element is excluded from the returned symmetric difference.

const sharedObj = { id: 1 };

const array1 = [sharedObj, { id: 2 }];
const array2 = [sharedObj, { id: 3 }];

const result = _.xor(array1, array2);
// result: [{ id: 2 }, { id: 3 }]

Here, sharedObj is omitted from the final array because its reference exists in both input sets.

Achieving Deep Structural Difference with _.xorWith

To compare objects based on their properties rather than their reference identity, use _.xorWith along with Lodash’s deep equality comparator, _.isEqual.

const objA = { id: 1, name: 'Alpha' };
const objB = { id: 1, name: 'Alpha' };

const result = _.xorWith([objA], [objB], _.isEqual);
// result: []

By passing _.isEqual as the comparator, Lodash evaluates structural contents recursively, ensuring that distinct references with identical data cancel each other out as expected.