Lodash _.uniqWith() method
What you’ll learn
- How
_.uniqWith(array, comparator)removes duplicates using a binary predicate instead of projections alone. - Why comparators follow the same
(arrVal, othVal)contract asunionWithanddifferenceWith. - When _.uniqBy() or plain _.uniq() stays simpler than bespoke compares.
- Try each example in the editor (
?tryit=1,2,3) with Lodash from a CDN.
Prerequisites
Read _.uniqBy() first—if a single iteratee fully captures equality, you rarely need the heavier comparator path.
- You are comfortable writing pure functions that compare two values at a time.
- You can open Try-it labs or run snippets locally.
Overview
_.uniqWith closes the gap when duplicates need contextual logic—deep structural equality, fuzzy numeric bands, or case-insensitive tokens inside one noisy array.
Binary compares
You decide when two elements collide—no forced single-field projection.
First wins
Earlier survivors remain as-is while later duplicates disappear.
Immutable source
The original array stays intact for auditing or retries.
Syntax
_.uniqWith(array, comparator) - array: collection to dedupe.
- comparator: invoked as
(arrVal, othVal); truthy means treat the pair as duplicates. - Returns: new array containing first occurrences under your comparator.
Rows merged by shared sku
Different object shapes still collide when the comparator narrows on a stable identifier inside one list.
import uniqWith from "lodash/uniqWith";
uniqWith(
[
{ sku: "A1", qty: 5 },
{ sku: "B2" },
{ sku: "A1", note: "duplicate" }
],
(a, b) => a.sku === b.sku
);
// → [{ sku: "A1", qty: 5 }, { sku: "B2" }] Deep structure with _.isEqual
Distinct references with identical nested payloads collapse when Lodash deems them deeply equivalent.
import uniqWith from "lodash/uniqWith";
import isEqual from "lodash/isEqual";
uniqWith(
[{ id: 7, meta: { tier: "gold" } }, { id: 9 }, { id: 7, meta: { tier: "gold" } }],
isEqual
);
// → [{ id: 7, meta: { tier: "gold" } }, { id: 9 }] Case-insensitive strings
Primitives compare fine—fold casing once inside the comparator without wrapping strings in objects.
import uniqWith from "lodash/uniqWith";
uniqWith(["a", "B", "A", "b"], (x, y) => x.toLowerCase() === y.toLowerCase());
// → ["a", "B"] 📋 _.uniqWith vs uniq, uniqBy, unionWith
| API | Equality signal | Best when |
|---|---|---|
_.uniqWith(array, comparator) | Binary predicate | Custom pairwise rules on one array |
_.uniq(array) | SameValueZero | Primitive/reference-stable uniqueness |
_.uniqBy(array, iteratee) | Projected scalar keys | Single-field fingerprints suffice |
_.unionWith(...arrays, comparator) | Binary predicate | Multiple arrays merged before dedupe |
Pitfalls to avoid
Comparator cost
Rich predicates such as deep equality can dominate runtime—measure large batches before production.
Asymmetric compares
Weird predicates that are not symmetric produce confusing survivors—keep rules predictable.
Freshness policy
First occurrence wins—reverse or sort upstream when newer twins should replace older ones.
❓ FAQ
Summary
- Purpose:
_.uniqWith(array, comparator)dedupes using your binary predicate while preserving first-seen order. - Contrast: prefer uniqBy when a lone iteratee expresses uniqueness cleanly.
- Next: Lodash _.unzip(), _.uniqBy() (previous), or the array methods hub.
The comparator matches _.unionWith() and _.differenceWith(): return truthy when two values should count as the same logical row—even when references or projections disagree.
6 people found this page helpful
