Find Unique Numbers

Removing Duplicates

Finding unique numbers (or removing duplicates) is a common programming task. JavaScript provides several approaches with different performance characteristics.

Try Finding Unique Numbers

Code Editor
Console Output
Click "Run" to execute your code...

Method 1: Using Set (Best Method)

// Set automatically removes duplicates
function getUniqueNumbers1(arr) {
  return [...new Set(arr)];
}

const numbers = [1, 2, 2, 3, 4, 4, 5, 1, 3];
console.log(getUniqueNumbers1(numbers)); // [1, 2, 3, 4, 5]

// Alternative syntax
function getUniqueNumbers1b(arr) {
  return Array.from(new Set(arr));
}

console.log(getUniqueNumbers1b([5, 5, 5, 6, 7, 7])); // [5, 6, 7]

// One-liner
const unique = arr => [...new Set(arr)];
console.log(unique([1, 1, 2, 2, 3, 3])); // [1, 2, 3]

// Works with any array type
const words = ['apple', 'banana', 'apple', 'orange', 'banana'];
console.log([...new Set(words)]); // ['apple', 'banana', 'orange']

// Explanation:
// 1. new Set(arr) - creates Set from array (removes duplicates)
// 2. [...Set] or Array.from(Set) - converts Set back to array

// Performance: O(n) - very fast!

Method 2: Using filter and indexOf

function getUniqueNumbers2(arr) {
  return arr.filter((num, index) => {
    return arr.indexOf(num) === index;
  });
}

const numbers = [1, 2, 2, 3, 4, 4, 5];
console.log(getUniqueNumbers2(numbers)); // [1, 2, 3, 4, 5]

// More concise
function getUniqueNumbers2b(arr) {
  return arr.filter((num, index) => arr.indexOf(num) === index);
}

console.log(getUniqueNumbers2b([1, 1, 2, 3, 2, 4])); // [1, 2, 3, 4]

// Explanation:
// indexOf returns the FIRST occurrence of an element
// If current index matches first occurrence, it's unique
// Example: [1, 2, 2, 3]
//   - index 0: indexOf(1) = 0 āœ“ (keep)
//   - index 1: indexOf(2) = 1 āœ“ (keep)
//   - index 2: indexOf(2) = 1 āœ— (remove, duplicate)
//   - index 3: indexOf(3) = 3 āœ“ (keep)

// Performance: O(n²) - slower for large arrays
// indexOf is called for each element, and indexOf itself is O(n)

Method 3: Using reduce

function getUniqueNumbers3(arr) {
  return arr.reduce((unique, num) => {
    if (!unique.includes(num)) {
      unique.push(num);
    }
    return unique;
  }, []);
}

const numbers = [1, 2, 2, 3, 4, 4, 5];
console.log(getUniqueNumbers3(numbers)); // [1, 2, 3, 4, 5]

// Alternative with ternary
function getUniqueNumbers3b(arr) {
  return arr.reduce((unique, num) => 
    unique.includes(num) ? unique : [...unique, num],
    []
  );
}

console.log(getUniqueNumbers3b([1, 1, 2, 3, 2])); // [1, 2, 3]

// Using object as accumulator (for counting)
function getUniqueWithCount(arr) {
  return arr.reduce((acc, num) => {
    acc[num] = (acc[num] || 0) + 1;
    return acc;
  }, {});
}

console.log(getUniqueWithCount([1, 2, 2, 3, 3, 3]));
// { 1: 1, 2: 2, 3: 3 }

// Get unique from the count object
const counts = getUniqueWithCount([1, 2, 2, 3]);
const uniqueNums = Object.keys(counts).map(Number);
console.log(uniqueNums); // [1, 2, 3]

// Performance: O(n²) - includes is O(n) for each element

Method 4: Using Object/Map as Hash

// Using Object
function getUniqueNumbers4(arr) {
  const seen = {};
  const result = [];
  
  for (const num of arr) {
    if (!seen[num]) {
      seen[num] = true;
      result.push(num);
    }
  }
  
  return result;
}

const numbers = [1, 2, 2, 3, 4, 4, 5];
console.log(getUniqueNumbers4(numbers)); // [1, 2, 3, 4, 5]

// Using Map (better for non-string keys)
function getUniqueNumbers4b(arr) {
  const seen = new Map();
  const result = [];
  
  for (const num of arr) {
    if (!seen.has(num)) {
      seen.set(num, true);
      result.push(num);
    }
  }
  
  return result;
}

console.log(getUniqueNumbers4b([1, 1, 2, 3, 2])); // [1, 2, 3]

// Get unique and preserve order
function getUniqueOrdered(arr) {
  const map = new Map();
  
  for (const num of arr) {
    if (!map.has(num)) {
      map.set(num, true);
    }
  }
  
  return Array.from(map.keys());
}

console.log(getUniqueOrdered([3, 1, 2, 1, 3])); // [3, 1, 2]

// Performance: O(n) - very fast, but uses extra memory

Method 5: Nested Loops (Not Recommended)

// Using nested loops
function getUniqueNumbers5(arr) {
  const result = [];
  
  for (let i = 0; i < arr.length; i++) {
    let isDuplicate = false;
    
    // Check if already in result
    for (let j = 0; j < result.length; j++) {
      if (arr[i] === result[j]) {
        isDuplicate = true;
        break;
      }
    }
    
    if (!isDuplicate) {
      result.push(arr[i]);
    }
  }
  
  return result;
}

const numbers = [1, 2, 2, 3, 4, 4, 5];
console.log(getUniqueNumbers5(numbers)); // [1, 2, 3, 4, 5]

// Alternative approach
function getUniqueNumbers5b(arr) {
  const result = [];
  
  for (const num of arr) {
    if (!result.includes(num)) {
      result.push(num);
    }
  }
  
  return result;
}

console.log(getUniqueNumbers5b([1, 1, 2, 3, 2])); // [1, 2, 3]

// Performance: O(n²) - slowest method, avoid for large arrays

Bonus: Advanced Cases

// Find duplicates (opposite of unique)
function findDuplicates(arr) {
  const seen = new Set();
  const duplicates = new Set();
  
  for (const num of arr) {
    if (seen.has(num)) {
      duplicates.add(num);
    } else {
      seen.add(num);
    }
  }
  
  return Array.from(duplicates);
}

console.log(findDuplicates([1, 2, 2, 3, 4, 4, 5])); // [2, 4]

// Count occurrences
function countOccurrences(arr) {
  return arr.reduce((acc, num) => {
    acc[num] = (acc[num] || 0) + 1;
    return acc;
  }, {});
}

console.log(countOccurrences([1, 2, 2, 3, 3, 3]));
// { 1: 1, 2: 2, 3: 3 }

// Find numbers appearing exactly once
function findUnique(arr) {
  const counts = countOccurrences(arr);
  return Object.keys(counts)
    .filter(key => counts[key] === 1)
    .map(Number);
}

console.log(findUnique([1, 2, 2, 3, 4, 4, 5])); // [1, 3, 5]

// Unique objects by property
const users = [
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' },
  { id: 1, name: 'John Doe' },
  { id: 3, name: 'Bob' }
];

function uniqueByProperty(arr, prop) {
  const seen = new Map();
  return arr.filter(item => {
    if (seen.has(item[prop])) {
      return false;
    }
    seen.set(item[prop], true);
    return true;
  });
}

console.log(uniqueByProperty(users, 'id'));
// [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }, { id: 3, name: 'Bob' }]

// Performance comparison
const largeArray = Array.from({ length: 100000 }, () => 
  Math.floor(Math.random() * 10000)
);

console.time('Set method');
[...new Set(largeArray)];
console.timeEnd('Set method');

console.time('Filter method');
largeArray.filter((n, i) => largeArray.indexOf(n) === i);
console.timeEnd('Filter method');

console.time('Map method');
const map = new Map();
largeArray.forEach(n => map.set(n, true));
Array.from(map.keys());
console.timeEnd('Map method');

// Result: Set is fastest, followed by Map, then filter

Key Points

  • Set is the best method: simple, fast O(n), and readable
  • filter + indexOf is intuitive but slow O(n²) for large arrays
  • Map/Object hash is fast O(n) but uses extra memory
  • Nested loops are slowest - avoid for production code
  • Choose method based on: array size, readability needs, memory constraints
  • Set preserves insertion order (ES6+)
  • For objects, use Map with custom key extraction