JavaScript array unique: Set, filter, reduce, Map, and NaN pitfalls

How to dedupe arrays in JavaScript: javascript array unique, js array unique, javascript unique array, distinct array javascript with Set spread, Array.from, filter plus indexOf, reduce plus includes, for loops, Map by key, NaN pitfalls with indexOf, and complexity notes. Snippets include short expected-output notes from Node runs.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

JavaScript array unique: Set, filter, reduce, Map, and NaN pitfalls

A javascript array unique (or js unique array) result removes duplicate entries while usually keeping first-seen order. The standard one-liner is [...new Set(arr)]. For javascript array distinct / distinct array javascript with objects, you either rely on reference identity in a Set, or use a Map keyed by a field. For background on Set itself, see HashSet in JavaScript (Set); for removing a known element from a list (different problem), see remove element from array in JavaScript.

Tested on: Node.js v20.18.2. A short note after each snippet describes what you should see in the console.


Quick reference

Set is the default primitive deduper (NaN-safe, amortized O(n)); reach for Map when uniqueness is defined by a field, not object identity.

Approach Typical complexity Notes
[...new Set(arr)] O(n) Best default for primitives; handles NaN once
filter + indexOf O(n²) Fails for NaN as written
reduce + includes O(n²) Clear, fine for small arrays
Map by key O(n) Unique by property, not by reference

Set + spread — array unique javascript

Set removes duplicates using SameValueZero equality and preserves insertion order when iterated.

javascript
const arr = [1, 2, 2, 3, 3, 4, 5];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr);
Output

You should see one line like: [ 1, 2, 3, 4, 5 ].

Same result with Array.from (handy if you prefer a factory style):

javascript
console.log(Array.from(new Set([1, 2, 2])));
Output

You should see one line like: [ 1, 2 ].

The spread operator expands the Set iterator into a new array; see also creating numeric ranges as arrays when building inputs.


filter + indexOf — keep first occurrence (js array unique)

Keep the first index where indexOf still points at the current element; later duplicates fail the equality check.

javascript
const arr = [45, 67, 67, 89, 67, 69];
const uniqueArr = arr.filter((element, index, self) => {
  return index === self.indexOf(element);
});
console.log(uniqueArr);
Output

You should see one line like: [ 45, 67, 89, 69 ].

Caveat: this pattern does not work for NaN, because indexOf(NaN) is always -1:

javascript
console.log([...new Set([NaN, NaN, NaN])]);
const a = [NaN, NaN];
console.log(a.filter((e, i, s) => i === s.indexOf(e)));
Output

You should see 2 lines: [ NaN ], then [].

For NaN, prefer Set or something like findIndex((x) => Object.is(x, e)).


reduce + includes

Build an accumulator array and skip values already present—simple to read but includes scans the accumulator each time.

javascript
const arr = [45, 67, 67, 89, 34, 34, 69];
const uniqueArr = arr.reduce((acc, each) => {
  if (!acc.includes(each)) {
    acc.push(each);
  }
  return acc;
}, []);

console.log(uniqueArr);
Output

You should see one line like: [ 45, 67, 89, 34, 69 ].

Still O(n²) worst case because includes scans the accumulator.


for loop + includes

Imperative style matches the reduce pattern: push only when the running list does not already contain the value.

javascript
const myArray = [1, 2, 3, 2, 4, 1, 5];
const uniqueArray = [];
for (let i = 0; i < myArray.length; i++) {
  if (!uniqueArray.includes(myArray[i])) {
    uniqueArray.push(myArray[i]);
  }
}
console.log(uniqueArray);
Output

You should see one line like: [ 1, 2, 3, 4, 5 ].

See JavaScript for loop with index for loop basics.


Distinct by a key — javascript array distinct for objects

Set treats object literals as distinct references. To collapse by id, use a Map (last occurrence wins here):

javascript
const objs = [{ id: 1 }, { id: 1 }, { id: 2 }];
console.log([...new Map(objs.map((o) => [o.id, o])).values()]);
Output

You should see one line like: [ { id: 1 }, { id: 2 } ].


Summary

[...new Set(arr)] covers most “unique primitives” tickets; indexOf filters fail on NaN, and objects need Map keyed by something stable—not reference equality in a Set unless that is what you mean.

  • Prefer [...new Set(arr)] or Array.from(new Set(arr)) for primitive dedupe with first-seen order.
  • Avoid indexOf-based uniqueness for values that include NaN; Set handles SameValueZero correctly.
  • reduce/for + includes is readable but quadratic; fine for small inputs.
  • For objects, dedupe by a stable key with Map, not reference identity in a Set.

References

MDN references for Set, Map, and array iteration helpers used in javascript unique array recipes.


Frequently Asked Questions

1. What is the easiest way to get a javascript array unique list?

Spread a Set: const unique = [...new Set(arr)]. Set removes duplicates using SameValueZero equality and preserves insertion order when you iterate it.

2. js array unique while keeping the first occurrence of each value?

Set iteration follows first insertion order, so [...new Set(arr)] keeps the first time each value appeared.

3. Why does my filter with indexOf fail for NaN for javascript unique array?

Array.prototype.indexOf uses SameValue and cannot find NaN. indexOf(NaN) is always -1, so index === indexOf(e) drops every NaN. Use a Set, or findIndex with Object.is.

4. How do I get array distinct javascript behavior for objects by an id field?

Use a Map keyed by the field, then take .values(). Duplicate keys overwrite earlier entries—decide if you want first or last wins.

5. Array unique javascript with Set—does it deep-duplicate objects?

No. Set stores references; two different object literals are always distinct. To treat objects as equal by a key, use Map or a serialized key strategy.

6. Is filter plus indexOf slower than Set for large arrays?

Usually yes. filter with indexOf is roughly O(n squared) because indexOf scans up to i each time. Set-based dedupe is amortized O(n).
Olorunfemi Akinlua

Boasting over five years of experience in JavaScript, specializing in technical content writing and UX design. With a keen focus on programming languages, he crafts compelling content and designs …