DEV Community

Cover image for Set Is Just Built Different ๐Ÿ˜ค๐Ÿ’ช
Matt Lewandowski
Matt Lewandowski

Posted on

7 4 4 4 5

Set Is Just Built Different ๐Ÿ˜ค๐Ÿ’ช

The title says it all ๐Ÿ˜ค. Let's talk about JavaScript's most slept-on data structure: Set. Everyone's out here using arrays and objects, but Set? Set is just built different โœจ.

I'll be real with you - I used to skip over Set entirely. "Just use an array," I thought. Or "objects are faster for lookups." But the more I worked with large-scale applications like Kollabe, the more I realized: Set is the secret sauce that combines the best of both worlds.

7 Times Set Proves It's Built Different ๐Ÿ˜ค

1: Lightning Fast Lookups AND Clean Syntax

Sets give you object-speed lookups with array-like syntax. It's literally the best of both worlds:

// Arrays: Clean syntax but slow lookups
const arr = [1, 2, 3];
const hasThree = arr.includes(3); // O(n) ๐ŸŒ

// Objects: Fast lookups but messy syntax
const obj = { 1: true, 2: true, 3: true };
const hasThreeObj = obj[3] === true; // O(1) but ๐Ÿคฎ

// Set: Clean AND fast ๐Ÿ˜Ž
const set = new Set([1, 2, 3]);
const hasThreeSet = set.has(3); // O(1) AND clean! ๐Ÿš€
Enter fullscreen mode Exit fullscreen mode

2: Automatic Deduplication That Just Works

Ever had to remove duplicates from an array? Set makes it trivial:

interface User {
  id: number;
  name: string;
}

const users: User[] = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 1, name: 'Alice' }, // Duplicate
];

// The old way: ๐Ÿค”
const uniqueByReduce = users.reduce((acc, user) => {
  if (!acc.some(u => u.id === user.id)) {
    acc.push(user);
  }
  return acc;
}, [] as User[]);

// The Set way: ๐Ÿ˜Ž
const uniqueUsers = [
  ...new Set(users.map(u => JSON.stringify(u)))
].map(u => JSON.parse(u));

// Even better with a custom serializer:
const uniqueById = [...new Set(users.map(u => u.id))]
  .map(id => users.find(u => u.id === id)!);
Enter fullscreen mode Exit fullscreen mode

3: Set Operations That Make Sense

Want to find common elements or differences between collections? Set operations are actually readable:

const backend = new Set(['alice', 'bob', 'charlie']);
const frontend = new Set(['bob', 'charlie', 'dave']);

// Intersection (users who do both)
const fullstack = new Set(
  [...backend].filter(dev => frontend.has(dev))
);

// Difference (backend-only devs)
const backendOnly = new Set(
  [...backend].filter(dev => !frontend.has(dev))
);

// Union (all devs)
const allDevs = new Set([...backend, ...frontend]);

console.log([...fullstack]); // ['bob', 'charlie']
console.log([...backendOnly]); // ['alice']
console.log([...allDevs]); // ['alice', 'bob', 'charlie', 'dave']
Enter fullscreen mode Exit fullscreen mode

4: Type-Safe Enums That Actually Work

TypeScript enums are... controversial. But Sets can give you type-safe, runtime-safe enums:

const HttpMethods = new Set(['GET', 'POST', 'PUT', 'DELETE'] as const);
type HttpMethod = typeof HttpMethods extends Set<infer T> ? T : never;

function makeRequest(method: HttpMethod, url: string) {
  if (!HttpMethods.has(method)) {
    throw new Error('Invalid HTTP method');
  }
  // ... make request
}

// TypeScript error! ๐ŸŽ‰
makeRequest('YOLO', '/api');
Enter fullscreen mode Exit fullscreen mode

5: Memory Efficient Large Collections

Sets are more memory efficient than objects for large collections of unique values:

// Let's measure memory usage
function getMemoryUsage(collection: any): number {
  const start = process.memoryUsage().heapUsed;
  // Force garbage collection in Node
  global.gc && global.gc();
  return process.memoryUsage().heapUsed - start;
}

// Generate large dataset
const data = Array.from({ length: 1000000 }, (_, i) => i.toString());

// Compare memory usage
const asObject = data.reduce((acc, val) => ({ ...acc, [val]: true }), {});
const asSet = new Set(data);

console.log('Object memory:', getMemoryUsage(asObject));
console.log('Set memory:', getMemoryUsage(asSet));
// Set uses significantly less memory! ๐ŸŽ‰
Enter fullscreen mode Exit fullscreen mode

6: WeakSet: The Memory Management Secret Weapon

Like WeakMap, WeakSet lets you store object references without preventing garbage collection:

const objects = new WeakSet();

class MemoryIntensive {
  constructor() {
    this.largeData = new Array(1000000);
  }
}

{
  const temp = new MemoryIntensive();
  objects.add(temp);
  // temp can be garbage collected even though it's in the WeakSet!
}
// Memory is freed! ๐Ÿ—‘๏ธ
Enter fullscreen mode Exit fullscreen mode

7: The Ultimate Event Tracker

Sets are perfect for tracking complex states in event systems:

class EventTracker {
  private activeEvents = new Set<string>();
  private completedEvents = new Set<string>();

  startEvent(eventId: string) {
    if (this.activeEvents.has(eventId)) {
      throw new Error('Event already active');
    }
    this.activeEvents.add(eventId);
  }

  completeEvent(eventId: string) {
    if (!this.activeEvents.has(eventId)) {
      throw new Error('Event not active');
    }
    this.activeEvents.delete(eventId);
    this.completedEvents.add(eventId);
  }

  getActiveCount() {
    return this.activeEvents.size;
  }

  getCompletionRate() {
    return this.completedEvents.size / 
      (this.activeEvents.size + this.completedEvents.size);
  }
}
Enter fullscreen mode Exit fullscreen mode

Performance Showdown ๐Ÿƒโ€โ™‚๏ธ

Let's be real about performance. Here's how Set stacks up:

const size = 1000000;
const lookupTimes = 10000;

// Setup
const arr = Array.from({ length: size }, (_, i) => i);
const obj = Object.fromEntries(arr.map(n => [n, true]));
const set = new Set(arr);

console.time('Array lookups');
for (let i = 0; i < lookupTimes; i++) {
  arr.includes(size - 1);
}
console.timeEnd('Array lookups');

console.time('Object lookups');
for (let i = 0; i < lookupTimes; i++) {
  obj[size - 1] === true;
}
console.timeEnd('Object lookups');

console.time('Set lookups');
for (let i = 0; i < lookupTimes; i++) {
  set.has(size - 1);
}
console.timeEnd('Set lookups');
Enter fullscreen mode Exit fullscreen mode

The results? Set is consistently faster than Array.includes() and nearly as fast as object property lookups. But unlike objects, it maintains insertion order and has a clean API. Built different indeed ๐Ÿ˜ค.

TypeScript Magic โœจ

Set plays incredibly well with TypeScript:

// Literal types with Set
const ValidStates = new Set(['pending', 'active', 'complete'] as const);
type State = typeof ValidStates extends Set<infer T> ? T : never;

// Union types from Set
const NumberSet = new Set([1, 2, 3] as const);
type ValidNumber = typeof NumberSet extends Set<infer T> ? T : never;
// Type is exactly: 1 | 2 | 3

// Generic Set utilities
function isSuperset<T>(set: Set<T>, subset: Set<T>): boolean {
  for (const elem of subset) {
    if (!set.has(elem)) return false;
  }
  return true;
}
Enter fullscreen mode Exit fullscreen mode

When NOT to Use Set

Let's keep it ๐Ÿ’ฏ - Set isn't always the answer:

  1. When you need to serialize data frequently (JSON.stringify)
  2. When you need index-based access (use arrays)
  3. When you need key-value pairs (use Map)
  4. When memory is extremely constrained (arrays might be better)

Conclusion

Set is just built different. It combines the best parts of arrays and objects into something uniquely powerful. From lightning-fast lookups to automatic deduplication, from clean set operations to memory efficiency, Set proves itself as an essential tool in modern JavaScript.

Next time you reach for an array or object, ask yourself: "Could Set handle this better?" The answer might surprise you ๐Ÿ˜ค๐Ÿ’ช.

P.S. If you're looking for a place to practice these Set operations in a real project, check out Kollabe. It's where I learned just how clutch Set can be for real-time collaboration!

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

Image of Docusign

๐Ÿ› ๏ธ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more