Using Lodash differenceWith for Custom Array Comparisons

The Lodash _.differenceWith method allows developers to compare arrays using a custom comparator function instead of relying on default shallow equality. This makes it an essential tool when filtering complex datasets, such as arrays of objects, where values need to be evaluated based on specific properties or deep structural equality. This guide explains how _.differenceWith works and provides practical code examples demonstrating how to implement custom comparison logic in your JavaScript applications.

Syntax and Parameters

The syntax for _.differenceWith is:

_.differenceWith(array, [values], [comparator])

Why Use _.differenceWith Instead of _.difference?

Standard array difference methods like _.difference use the SameValueZero algorithm (similar to ===), which compares object references rather than their internal properties. When working with arrays of objects, _.difference fails to detect identical objects with different memory references:

const listA = [{ id: 1, name: 'Alice' }];
const listB = [{ id: 1, name: 'Alice' }];

// Returns [{ id: 1, name: 'Alice' }] because the references differ
_.difference(listA, listB); 

_.differenceWith solves this issue by letting you define the exact condition that determines whether two elements match.

Comparing Objects by a Specific Property

In many scenarios, you only need to check if a specific key—such as an ID—matches between two sets of objects.

const _ = require('lodash');

const currentUsers = [
  { id: 101, name: 'Alice' },
  { id: 102, name: 'Bob' },
  { id: 103, name: 'Charlie' }
];

const inactiveUsers = [
  { id: 102, name: 'Bob' }
];

// Keep only users whose IDs do not appear in inactiveUsers
const activeUsers = _.differenceWith(
  currentUsers,
  inactiveUsers,
  (userA, userB) => userA.id === userB.id
);

console.log(activeUsers);
// Output: [ { id: 101, name: 'Alice' }, { id: 103, name: 'Charlie' } ]

Deep Object Comparison with _.isEqual

If you need to filter out objects that are identical across all keys and nested properties, you can pass Lodash’s _.isEqual method directly as the comparator function:

const _ = require('lodash');

const baseInventory = [
  { sku: 'A1', stock: 10, details: { warehouse: 'East' } },
  { sku: 'B2', stock: 5, details: { warehouse: 'West' } },
  { sku: 'C3', stock: 0, details: { warehouse: 'East' } }
];

const processedInventory = [
  { sku: 'A1', stock: 10, details: { warehouse: 'East' } }
];

const remainingInventory = _.differenceWith(
  baseInventory,
  processedInventory,
  _.isEqual
);

console.log(remainingInventory);
// Output:
// [
//   { sku: 'B2', stock: 5, details: { warehouse: 'West' } },
//   { sku: 'C3', stock: 0, details: { warehouse: 'East' } }
// ]

Using Multiple Exclusion Arrays

_.differenceWith accepts multiple arrays of values to filter out. The comparator will evaluate items against all subsequent arrays:

const numbers = [1.2, 2.4, 3.7, 4.1];
const excludeList1 = [1.8];
const excludeList2 = [3.2];

// Exclude numbers with the same rounded value
const filtered = _.differenceWith(
  numbers,
  excludeList1,
  excludeList2,
  (a, b) => Math.floor(a) === Math.floor(b)
);

console.log(filtered);
// Output: [ 2.4, 4.1 ]

Performance Considerations

Because _.differenceWith executes the comparator function by iterating through elements in the exclusion arrays for each item in the base array, its time complexity is \(O(n \times m)\) where \(n\) is the length of the source array and \(m\) is the total length of the comparison arrays. For very large datasets with simple unique keys, mapping to a Set of primitives may provide better performance than running nested iterations.