How to get all unique values in a JavaScript array ?

To get all unique values in a JavaScript array, here’s a detailed guide with examples, edge cases, and explanations for different scenarios:

1. Using Set (ES6+)

Set is a built-in object that stores unique values, making it the most efficient and modern approach.

Example:

const arr = [1, 2, 2, "hello", "hello", NaN, NaN, {a: 1}, {a: 1}];
const unique = [...new Set(arr)];

console.log(unique);
// [1, 2, "hello", NaN, {a: 1}, {a: 1}]

Key Points:

  • Handles NaN: Treats all NaN values as identical (unlike other methods).
  • Objects: Treats objects as unique unless they are the same reference.
  • Order: Preserves the order of the first occurrence.

Edge Cases:

  • Mixed types (e.g., 1 vs "1") are treated as distinct.
  • NaN is deduplicated, but objects with identical properties are not.

2. Using filter + indexOf

Filters elements based on their first occurrence in the array.

Example:

const arr = [1, 2, 2, "hello", "hello", NaN, NaN];
const unique = arr.filter((item, index) => arr.indexOf(item) === index);

console.log(unique);
// [1, 2, "hello", NaN, NaN] ❌ Fails to deduplicate NaN!

Key Points:

  • Fails for NaN: indexOf(NaN) returns -1, so NaN values are not deduplicated.
  • Objects: Works only if objects are the same reference.

3. Using reduce

Accumulates unique values by checking against an array.

Example:

const arr = [1, 2, 2, "hello", "hello", NaN, NaN];
const unique = arr.reduce((acc, item) => {
  return acc.includes(item) ? acc : [...acc, item];
}, []);

console.log(unique);
// [1, 2, "hello", NaN] ✅ Handles NaN!

Key Points:

  • Handles NaN: includes() correctly detects NaN.
  • Objects: Treats objects as unique unless they are the same reference.
  • Performance: Inefficient for large arrays due to O(n²) time complexity.

4. Using an Object (Primitives Only)

Uses an object to track seen values (keys are coerced to strings).

Example:

const arr = [1, 2, 2, "1", NaN, NaN, {a: 1}];
const seen = {};
const unique = arr.filter(item => {
  const key = typeof item + JSON.stringify(item); // Handle edge cases
  return seen.hasOwnProperty(key) ? false : (seen[key] = true);
});

console.log(unique);
// [1, 2, "1", NaN, {a: 1}] ✅ Deduplicates NaN but treats 1 and "1" as unique!

Key Points:

  • Primitives Only: Coerces keys to strings (e.g., 1 and "1" become "number:1" vs "string:\"1\"").
  • Custom Key Logic: Use typeof and JSON.stringify to avoid collisions (e.g., 1 vs "1").
  • Fails for Objects: Generic objects are treated as unique even with identical properties.

5. Advanced: Deduplicate Objects by Property

To deduplicate objects based on a specific property (e.g., id):

Example:

const users = [
  {id: 1, name: "Alice"},
  {id: 1, name: "Alice"},
  {id: 2, name: "Bob"}
];

const seen = new Map();
const uniqueUsers = users.filter((user) => {
  return seen.has(user.id) ? false : seen.set(user.id, true);
});

console.log(uniqueUsers);
// [{id: 1, name: "Alice"}, {id: 2, name: "Bob"}]

Key Points:

  • Uses Map to track unique identifiers (e.g., id).
  • Works for objects with a unique key.

6. Handling Mixed Data Types

Example:

const mixedArr = [1, "1", true, "true", null, undefined, NaN, NaN];
const unique = [...new Set(mixedArr)];

console.log(unique);
// [1, "1", true, "true", null, undefined, NaN]

Key Points:

  • Set treats all values strictly (1 !== "1").

Performance Comparison

MethodTime ComplexityHandles NaN?Handles Objects?
SetO(n)✅ (by reference)
filter + indexOfO(n²)✅ (by reference)
reduceO(n²)✅ (by reference)
Object TrackingO(n)

Final Recommendations

  • Modern Browsers: Use Set for simplicity and speed.
  • Legacy Code: Use reduce or filter + indexOf (with NaN checks).
  • Objects by Property: Combine Map with a unique identifier.
  • Mixed Data Types: Always test with edge cases like 1 vs "1" or NaN.

Example for Legacy Browsers:

function getUnique(arr) {
  const seen = {};
  return arr.filter((item) => {
    const key = typeof item + JSON.stringify(item);
    return seen.hasOwnProperty(key) ? false : (seen[key] = true);
  });
}

This handles most primitives but not objects with identical properties.

Leave a Reply

Your email address will not be published. Required fields are marked *