Advanced JavaScript (ES6+)

Sets - Unique Value Collections

13 min Lesson 21 of 40

Sets - Unique Value Collections

ES6 introduced the Set object, a powerful data structure for storing unique values of any type. Sets automatically ensure that each value appears only once, making them perfect for removing duplicates and performing mathematical set operations.

What is a Set?

A Set is a collection of values where each value must be unique. Unlike arrays, Sets:

  • Automatically remove duplicate values
  • Can contain any type of value (primitives or objects)
  • Maintain insertion order
  • Provide efficient methods for checking value existence
Key Fact: Sets use the SameValueZero algorithm for equality checks, which treats NaN as equal to NaN (unlike === comparison).

Creating Sets

You can create Sets in multiple ways:

// Empty Set const emptySet = new Set(); // Set from array const numbers = new Set([1, 2, 3, 4, 5]); // Set with duplicates removed automatically const unique = new Set([1, 2, 2, 3, 3, 3, 4]); console.log(unique); // Set {1, 2, 3, 4} // Set from string (each character) const letters = new Set('hello'); console.log(letters); // Set {'h', 'e', 'l', 'o'} // Set with mixed types const mixed = new Set([1, 'text', true, null, {id: 1}]);

Set Methods

Sets provide several methods for adding, checking, and removing values:

const fruits = new Set(); // add() - Add a value fruits.add('apple'); fruits.add('banana'); fruits.add('orange'); fruits.add('apple'); // Duplicate, won't be added console.log(fruits.size); // 3 // has() - Check if value exists console.log(fruits.has('apple')); // true console.log(fruits.has('grape')); // false // delete() - Remove a value fruits.delete('banana'); console.log(fruits.has('banana')); // false // clear() - Remove all values fruits.clear(); console.log(fruits.size); // 0
Tip: The add() method returns the Set itself, allowing for method chaining: set.add(1).add(2).add(3)

Iterating Over Sets

Sets are iterable and can be looped through in several ways:

const colors = new Set(['red', 'green', 'blue']); // for...of loop for (const color of colors) { console.log(color); } // forEach method colors.forEach((value, valueAgain, set) => { console.log(value); // Note: value appears twice for consistency with Map }); // Convert to array const colorArray = [...colors]; const colorArray2 = Array.from(colors); // Using iterator methods console.log(colors.keys()); // SetIterator console.log(colors.values()); // SetIterator console.log(colors.entries()); // SetIterator of [value, value]

Removing Duplicates from Arrays

One of the most common uses of Sets is removing duplicate values from arrays:

// Remove duplicates const numbers = [1, 2, 2, 3, 3, 3, 4, 4, 5]; const unique = [...new Set(numbers)]; console.log(unique); // [1, 2, 3, 4, 5] // Remove duplicate strings const words = ['hello', 'world', 'hello', 'javascript']; const uniqueWords = [...new Set(words)]; console.log(uniqueWords); // ['hello', 'world', 'javascript'] // Remove duplicate objects (by reference) const obj1 = {id: 1}; const obj2 = {id: 2}; const objects = [obj1, obj2, obj1, {id: 1}]; // Last one is different reference const uniqueObjects = [...new Set(objects)]; console.log(uniqueObjects.length); // 3 (last {id: 1} is different object)
Important: Sets compare objects by reference, not by value. Two objects with identical properties are considered different unless they reference the same object.

Set Operations

Sets are perfect for mathematical set operations like union, intersection, and difference:

const setA = new Set([1, 2, 3, 4]); const setB = new Set([3, 4, 5, 6]); // Union - All unique values from both sets const union = new Set([...setA, ...setB]); console.log(union); // Set {1, 2, 3, 4, 5, 6} // Intersection - Values present in both sets const intersection = new Set([...setA].filter(x => setB.has(x))); console.log(intersection); // Set {3, 4} // Difference - Values in setA but not in setB const difference = new Set([...setA].filter(x => !setB.has(x))); console.log(difference); // Set {1, 2} // Symmetric Difference - Values in either set but not both const symmetricDiff = new Set([ ...[...setA].filter(x => !setB.has(x)), ...[...setB].filter(x => !setA.has(x)) ]); console.log(symmetricDiff); // Set {1, 2, 5, 6} // Subset - Check if setA is subset of setB const isSubset = [...setA].every(x => setB.has(x)); console.log(isSubset); // false

Practical Examples

// Example 1: Track unique visitors const uniqueVisitors = new Set(); function trackVisitor(userId) { uniqueVisitors.add(userId); console.log(`Total unique visitors: ${uniqueVisitors.size}`); } trackVisitor('user1'); trackVisitor('user2'); trackVisitor('user1'); // Duplicate, won't increase count // Example 2: Find unique tags const articles = [ { tags: ['javascript', 'programming', 'web'] }, { tags: ['css', 'design', 'web'] }, { tags: ['javascript', 'es6', 'programming'] } ]; const allTags = new Set(); articles.forEach(article => { article.tags.forEach(tag => allTags.add(tag)); }); console.log([...allTags]); // ['javascript', 'programming', 'web', 'css', 'design', 'es6'] // Example 3: Remove duplicates while maintaining case function getUniqueCaseInsensitive(strings) { const seen = new Set(); return strings.filter(str => { const lower = str.toLowerCase(); if (seen.has(lower)) { return false; } seen.add(lower); return true; }); } const names = ['John', 'jane', 'JOHN', 'Jane', 'Bob']; console.log(getUniqueCaseInsensitive(names)); // ['John', 'jane', 'Bob']

Performance Characteristics

Understanding Set performance helps you choose the right data structure:

Time Complexity: - add(): O(1) - Constant time - has(): O(1) - Constant time - delete(): O(1) - Constant time - clear(): O(n) - Linear time - Size: O(1) - Constant time Comparison with Arrays: - Checking if value exists: Set.has() is O(1) vs Array.includes() is O(n) - Adding unique values: Set.add() is O(1) vs Array.push() with duplicate check is O(n) - Removing values: Set.delete() is O(1) vs Array.splice() is O(n)
Best Practice: Use Sets when you need to maintain unique values or frequently check for value existence. Use Arrays when you need indexed access or duplicate values.

Set vs Array Comparison

// When to use Set const uniqueIds = new Set([1, 2, 3]); // Need unique values uniqueIds.has(2); // Fast lookup - O(1) uniqueIds.add(4); // Fast insertion - O(1) // When to use Array const scores = [95, 88, 95, 92]; // Duplicates are meaningful scores[0]; // Need indexed access scores.map(x => x * 1.1); // Need array methods

Practice Exercise:

Challenge: Create a function that finds common friends between two users.

function findCommonFriends(user1Friends, user2Friends) { const set1 = new Set(user1Friends); const commonFriends = user2Friends.filter(friend => set1.has(friend)); return commonFriends; } const alice = ['Bob', 'Charlie', 'David', 'Eve']; const john = ['Charlie', 'Eve', 'Frank', 'Grace']; console.log(findCommonFriends(alice, john)); // Output: ['Charlie', 'Eve']

Try it yourself: Extend this to find friends unique to each user (symmetric difference).

Summary

In this lesson, you learned:

  • Sets store unique values and automatically remove duplicates
  • Set methods: add(), has(), delete(), clear(), and size property
  • Sets can be iterated with for...of, forEach, and spread operator
  • Sets are perfect for removing array duplicates and mathematical operations
  • Set operations: union, intersection, difference, and subset checks
  • Sets provide O(1) performance for add, has, and delete operations
  • Choose Sets for unique values and fast lookups, Arrays for indexed access
Next Up: In the next lesson, we'll explore Maps - key-value pairs with any type of key!