Removing Duplicates from Arrays in JavaScript Guide

Learn four ways to remove duplicate values from JavaScript arrays: Set, filter, reduce, and forEach. Compare approaches for primitives and objects.

6 min read

JavaScript gives you several ways to remove duplicate values from an array. The best approach depends on whether your array contains primitives (numbers, strings) or objects, and how large the array is.

1. Set -- The Modern Standard (Primitives)

A Set is a collection that only stores unique values. Converting an array to a Set and back removes all duplicates in one shot:

javascriptjavascript
const numbers = [1, 2, 2, 3, 3, 3, 4];
 
const unique = [...new Set(numbers)];
 
console.log(unique); // [1, 2, 3, 4]

This works for any primitive type, and a Set can even hold a mix of different types in the same collection:

javascriptjavascript
const mixed = [1, "hello", 1, "hello", true, true, null, null];
 
const unique = [...new Set(mixed)];
 
console.log(unique); // [1, "hello", true, null]

Set uses the SameValueZero algorithm for comparison. It correctly handles NaN, treats +0 and -0 as equal, and works with undefined and null.

The Set approach is the shortest, fastest, and most readable. It should be your default choice for arrays of primitives.

Set removes duplicates by storing only unique values

The array passes through a Set, which silently drops duplicates. Spreading the Set back into an array gives you the result.

2. filter() with indexOf() (Works Everywhere)

Before Set became common, filter() combined with indexOf() was the standard approach:

javascriptjavascript
const items = ["a", "b", "a", "c", "b"];
 
const unique = items.filter((item, index) => items.indexOf(item) === index);
 
console.log(unique); // ["a", "b", "c"]

The logic: keep an element only if its first occurrence in the array is at the current index. If indexOf(item) returns an earlier index, the element is a duplicate and is filtered out.

This approach works in older environments that do not support Set, but it is O(n²) because indexOf() scans the array for each element. For large arrays, prefer Set.

3. reduce() (Flexible but Verbose)

reduce() gives you full control over the deduplication logic:

javascriptjavascript
const numbers = [1, 2, 2, 3, 3, 3, 4];
 
const unique = numbers.reduce((acc, n) => {
  if (!acc.includes(n)) {
    acc.push(n);
  }
  return acc;
}, []);
 
console.log(unique); // [1, 2, 3, 4]

The accumulator starts as an empty array. Each element is added only if it is not already present. This is also O(n²) due to the includes() check inside the loop.

Use reduce() only when you need custom deduplication logic that Set cannot express directly. For the basics of reduce(), see JavaScript Array Reduce Method.

4. Deduplicating Arrays of Objects

Set does not help with object arrays because objects are compared by reference:

javascriptjavascript
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" } // Same id, different object reference
];
 
const unique = [...new Set(users)];
 
console.log(unique.length); // 3 (nothing removed!)

To deduplicate objects, use a Map keyed by a unique property, since a Map lets you look up and overwrite entries by that key directly:

javascriptjavascript
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" }
];
 
const unique = [...new Map(users.map(u => [u.id, u])).values()];
 
console.log(unique);
// [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]

Step by step:

Map each user to a key-value pair

users.map(u => [u.id, u]) creates a two-item array for each user, pairing that user's id with the full user object.

Build a Map from those pairs

new Map(...) stores entries by key. Duplicate keys overwrite, keeping the last value.

Extract the unique objects

.values() returns an iterator over the Map's values, one per unique key.

Convert back to an array

[...spread] converts that iterator into a real array.

For deeper object comparison (matching by all properties), use a JSON-based key:

javascriptjavascript
const uniqueByContent = [...new Map(
  users.map(u => [JSON.stringify(u), u])
).values()];

This treats objects as equal if they have the same JSON representation, regardless of which id field you consider the key. Watch out for property order: JSON.stringify() preserves key order, so two objects with identical properties written in a different order produce different strings and will not be treated as duplicates.

Performance Comparison

MethodComplexityBest for
SetO(n)Primitives (numbers, strings)
filter() + indexOf()O(n²)Legacy environments only
reduce() + includes()O(n²)Custom deduplication logic
Map by keyO(n)Arrays of objects

For primitives, Set is both the fastest and the shortest. For objects, Map by a unique key is the fastest. The filter() and reduce() approaches are quadratic and should only be used when Set or Map is unavailable.

Common Mistakes

Expecting Set to deduplicate objects:

javascriptjavascript
const a = { id: 1 };
const b = { id: 1 };
 
const arr = [a, b];
 
console.log([...new Set(arr)].length); // 2 (Set sees two different objects)

Set compares objects by reference, not by content. For object deduplication, use the Map pattern shown above.

Using Set on very large arrays without considering memory:

javascriptjavascript
// Set creates an intermediate data structure
const huge = [...new Set(massiveArray)];

For arrays with millions of items, the intermediate Set uses extra memory. If memory is a concern and the array is already sorted, use a single-pass loop:

javascriptjavascript
function dedupeSorted(arr) {
  return arr.filter((item, i) => i === 0 || item !== arr[i - 1]);
}
 
const sorted = [1, 1, 2, 2, 3, 3, 3];
console.log(dedupeSorted(sorted)); // [1, 2, 3]

This only works on sorted arrays and is O(n) with no extra data structure.

Now that you have mastered arrays, the next section covers objects, maps, and sets where you will learn about key-value data structures and how they complement arrays.

Rune AI

Rune AI

Key Insights

  • [...new Set(arr)] is the simplest and fastest way to deduplicate arrays of primitives.
  • Set comparison uses SameValueZero, which works for primitives but not for distinct objects with the same properties.
  • filter() with indexOf works for primitives but is O(n²) and slower for large arrays.
  • Use a Map keyed by a unique property to deduplicate arrays of objects.
  • For already-sorted arrays, a single-pass comparison approach is the most efficient.
RunePowered by Rune AI

Frequently Asked Questions

What is the fastest way to remove duplicates from an array?

Using new Set(arr) and spreading it back into an array with [...new Set(arr)] is the fastest approach for arrays of primitive values. It runs in O(n) time and is supported in all modern environments.

Does the Set approach work for removing duplicate objects?

No. Set uses SameValueZero comparison, which treats objects as equal only if they are the same reference. Two objects with identical properties are considered different by Set. For objects, use a Map keyed by a unique property like an ID.

Can I remove duplicates from a sorted array more efficiently?

Yes. If the array is already sorted, you can do a single pass comparing each element to the previous one, keeping it only if it differs. This is O(n) with no extra data structure.

Conclusion

Removing duplicates is a common task with several good solutions. For arrays of primitives, the Set approach is the clear winner -- it is the shortest, fastest, and most readable. For arrays of objects, use a Map keyed by a unique identifier to deduplicate by that property. Understanding the tradeoffs between these approaches lets you choose the right tool for your data.