How Lodash _.xor Computes Array Symmetric Difference
The _.xor method in the Lodash JavaScript library
calculates the symmetric difference across multiple arrays by returning
a new array of unique values that appear in only one of the provided
arrays. Unlike standard set operations that are limited to pairs of
sets, Lodash generalizes symmetric difference to multiple inputs by
filtering out any element that appears in two or more arrays. This
article examines the behavior of _.xor, explains its
internal algorithm using Lodash's core operations, and demonstrates how
it processes both two-array and multi-array inputs.
What is Symmetric Difference in Lodash?
In mathematical set theory, the symmetric difference of two sets \(A\) and \(B\) consists of elements that belong to either \(A\) or \(B\), but not both (\(A \Delta B = (A \setminus B) \cup (B \setminus A)\)).
When working with more than two sets, symmetric difference can mathematically be interpreted in two ways:
- Parity-based: Elements present in an odd number of sets.
- Exclusivity-based: Elements present in exactly one set.
Lodash adopts the exclusivity-based approach. When
_.xor is supplied with two or more arrays, it creates an
array of unique values that exist in exactly one of the given arrays,
excluding any element that occurs in multiple arrays.
Basic Syntax and Usage
import _ from 'lodash';
const array1 = [2, 1];
const array2 = [2, 3];
const result = _.xor(array1, array2);
console.log(result);
// => [1, 3]In this two-array example:
2appears in botharray1andarray2, so it is discarded.1exists only inarray1.3exists only inarray2.- The returned array is
[1, 3].
How the Algorithm Works Internally
Under the hood, Lodash executes _.xor using internal
utility functions including baseXor,
baseDifference, baseFlatten, and
baseUniq. The computation proceeds through the following
steps:
1. Handling Edge Cases
If no arrays are provided, _.xor returns an empty array
[]. If only one array is passed, Lodash passes it through
baseUniq to strip duplicate values and returns the
result.
2. Relative Difference Across All Other Arrays
When two or more arrays are passed, Lodash iterates through each
array in the arguments list. For each array at index i,
Lodash calculates its difference relative to every other array at index
j (where i !== j) using
baseDifference:
\[\text{exclusive}_i = \text{array}_i \setminus \bigcup_{j \neq i} \text{array}_j\]
Any item in array_i that is also found in another array
is eliminated.
3. Flattening and Deduplication
Once each array has been stripped of elements shared with other arrays:
- The remaining exclusive arrays are flattened into a single list
using a shallow flatten operation (
baseFlatten). - The flattened list is passed through
baseUniqto ensure that duplicate values within the same source array appear only once in the final output. - The resulting array preserves the relative order of elements based on their original appearance across the input arrays.
Multi-Array Example
The distinction of Lodash's exclusivity rule becomes apparent when working with three or more arrays:
const a = [1, 2];
const b = [2, 3];
const c = [1, 4];
console.log(_.xor(a, b, c));
// => [3, 4]Tracing the execution:
1appears inaandc(two arrays) \(\rightarrow\) excluded.2appears inaandb(two arrays) \(\rightarrow\) excluded.3appears only inb\(\rightarrow\) included.4appears only inc\(\rightarrow\) included.
The final result is [3, 4].
Comparison with
_.xorBy and _.xorWith
Lodash also provides custom-comparator variants of the symmetric difference algorithm:
_.xorBy: Accepts an iteratee invoked for each element to generate the criterion by which uniqueness and equality are computed (e.g., comparing objects by a specific property:_.xorBy(arr1, arr2, 'id'))._.xorWith: Accepts a custom comparator function to determine equality between items (e.g., using_.isEqualfor deep object comparison:_.xorWith(arr1, arr2, _.isEqual)).
Both variants follow the same algorithmic structure as
_.xor, but replace the default strict equality checks
(===) inside baseDifference and
baseUniq with the user-defined iteratee or comparator.