TechEarl

Remove Duplicate Values From an Array in JavaScript

Dedupe a JavaScript array. [...new Set(arr)] is the one-liner for primitives, but Set compares objects by reference, so arrays of objects need a Map keyed on a property. The gotchas with NaN and signed zero too.

Ishan Karunaratne⏱️ 6 min readUpdated
Share thisCopied
Remove duplicate values from a JavaScript array with new Set for primitives and a Map keyed on a property for arrays of objects, plus the NaN and signed-zero semantics.

To remove duplicate values from an array of primitives in JavaScript, wrap it in a Set and spread it back into an array:

javascript
const unique = [...new Set([1, 2, 2, 3, 3, 3])]; // [1, 2, 3]

That is the whole answer for numbers, strings, booleans, null, undefined, and symbols. A Set stores each value at most once, so building one from the array drops the repeats; the spread (...) turns it back into a plain array. It preserves first-seen order, which is the behavior you almost always want.

The rest of this page is the part the one-liner does not tell you: it silently fails on arrays of objects, because Set compares objects by reference, not by contents. If you have [{id: 1}, {id: 1}], Set keeps both. I have watched that "fix" ship and quietly do nothing more than once, so the object case gets its own section below.

Dedupe an array of primitives

The canonical form, and the one to reach for by default:

javascript
const ids = [4, 4, 8, 15, 16, 16, 23, 42, 42];
const unique = [...new Set(ids)]; // [4, 8, 15, 16, 23, 42]

new Set(ids) constructs a set from the array (one entry per distinct value). The spread copies those entries into a fresh array in insertion order. It works for any mix of primitives:

javascript
const tags = ["js", "css", "js", "html", "css"];
[...new Set(tags)]; // ["js", "css", "html"]

If you only need to count the distinct values or test membership, you do not need the spread back to an array at all. new Set(ids).size is the count, and set.has(x) is an O(1) membership check, far cheaper than array.includes(x) in a loop.

Set membership semantics: NaN and signed zero

Set decides whether two values are the same using the SameValueZero algorithm. It behaves exactly like === with one deliberate exception, and two cases matter for deduping:

  • NaN deduplicates. This is the one place SameValueZero diverges from ===. Under ===, NaN === NaN is false, so a naive includes/indexOf dedupe leaves duplicate NaNs in. SameValueZero treats every NaN as the same value, so Set collapses them.
  • +0 and -0 collapse to one entry. This is not a special case, +0 === -0 is already true, so SameValueZero agrees and you get a single zero out. It is worth knowing because Object.is (SameValue) is the comparison that keeps the two zeros apart.
javascript
[...new Set([NaN, NaN])]; // [NaN]    one entry
[...new Set([0, -0])];    // [0]      one entry

This is the right behavior for almost every real dataset, and it is one more reason to prefer Set over a hand-rolled filter(indexOf) loop, which gets the NaN case wrong.

Dedupe an array of objects: the reference trap

This is the gotcha that sends people to the search box. Set compares objects by reference, so two object literals with identical contents are different values to it:

javascript
const users = [
  { id: 1, name: "Ada" },
  { id: 2, name: "Linus" },
  { id: 1, name: "Ada" },
];

[...new Set(users)].length; // 3   nothing was removed

Each { id: 1, name: "Ada" } is a separate object in memory, so Set keeps all three. The fix is to dedupe by a key you actually care about. A Map keyed on that property is the cleanest way: building the map overwrites earlier entries with the same key, and .values() gives you back one object per key.

javascript
const byId = [...new Map(users.map((u) => [u.id, u])).values()];
// [ { id: 1, name: "Ada" }, { id: 2, name: "Linus" } ]

Read it inside-out: users.map(u => [u.id, u]) builds [key, value] pairs, new Map(...) keeps only the last pair per key, and .values() spread back into an array gives the deduped result. Note this keeps the last object seen for a given id. To keep the first instead, reverse before building the map, or guard with if (!map.has(u.id)) map.set(u.id, u) in a loop.

If you need to dedupe by the whole object rather than one field, the usual trick is to key on a serialized form:

javascript
const seen = new Map(items.map((it) => [JSON.stringify(it), it]));
const unique = [...seen.values()];

That works, but mind the caveat: JSON.stringify is key-order sensitive, so {a: 1, b: 2} and {b: 2, a: 1} serialize differently and will not dedupe against each other. It also drops undefined values and functions. For a small, fixed object shape it is fine; for arbitrary objects, pin down a canonical key (often a real id) instead of stringifying.

I wrote a small te-prefixed helper for the by-property case, since it comes up constantly:

javascript
const teUniqueBy = (arr, keyFn) =>
  [...new Map(arr.map((item) => [keyFn(item), item])).values()];

teUniqueBy(users, (u) => u.id); // deduped by id

The ES5 fallback

Array.from(new Set(arr)) is exactly equivalent to [...new Set(arr)] and predates spread syntax. You only need it in environments without spread, which in practice means very old transpile targets:

javascript
var unique = Array.from(new Set(arr));

In 2026 there is no reason to prefer it for compatibility (Set and Array.from both date to ES2015, and spread arrived with them). Reach for Array.from when you also want to map in the same pass, for example Array.from(new Set(arr), (x) => x * 2).

See also

Sources

Authoritative references this article was fact-checked against.

TagsJavaScriptarraysSetMapdedupeunique arrayremove duplicates

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

How to Remove Empty Values from an Array in PHP

Drop empty, null, or false values from a PHP array with array_filter and the right callback. Includes the '0 gets removed' gotcha, the array_values re-index pattern, multidimensional cleanup, and a performance comparison.

How to Merge Two Arrays in JavaScript

Merge two arrays in JavaScript three ways: copy-merge with spread [...a, ...b], merge in place with a.push(...b), or copy with a.concat(b). Which to pick by mutation, memory, and the spread arg-count limit that bites on very large arrays.