Diego Betto
Photo by Zdeněk Macháček on Unsplash

Diego Betto · September 19, 2026 · 4 min di lettura

JavaScript's new Set methods: union, intersection, difference, and the rest

The native Set operations that shipped in modern engines — union, intersection, difference, symmetricDifference, isSubsetOf, isSupersetOf, isDisjointFrom — with before/after examples.

Condividi:XLinkedInFacebookWhatsApp

For years, doing basic set theory in JavaScript — union, intersection, “what’s in A but not in B” — meant reaching for Array.filter()/includes() by hand, or pulling in a small utility library. That’s no longer necessary: modern JavaScript engines now ship these operations natively on Set.

ℹ️ Availability

These methods landed in recent versions of Chrome, Firefox, Safari, and Node.js. Check your target runtimes before relying on them without a polyfill — support is broad in 2026 but not universal across older environments you might still need to support.

The old way: array-based set operations

Before these methods existed, a union or intersection meant writing this by hand every time:

function union(a, b) {
  return new Set([...a, ...b]);
}

function intersection(a, b) {
  return new Set([...a].filter((x) => b.has(x)));
}

function difference(a, b) {
  return new Set([...a].filter((x) => !b.has(x)));
}

Correct, but verbose, and easy to get subtly wrong (union without deduplication, intersection with O(n²) complexity when both inputs are large). The new methods are both more concise and better optimized internally.

The new methods

const admins = new Set(["alice", "bob", "carol"]);
const editors = new Set(["bob", "carol", "dave"]);

admins.union(editors);
// Set(4) { 'alice', 'bob', 'carol', 'dave' }

admins.intersection(editors);
// Set(2) { 'bob', 'carol' }

admins.difference(editors);
// Set(1) { 'alice' } — in admins but not in editors

admins.symmetricDifference(editors);
// Set(2) { 'alice', 'dave' } — in exactly one of the two sets

admins.isSubsetOf(editors); // false
admins.isSupersetOf(new Set(["bob"])); // true
admins.isDisjointFrom(new Set(["zara"])); // true — no elements in common

Every one of these reads as exactly what it does — no filter callback to parse, no risk of accidentally mutating one of the input sets (union, intersection, difference, and symmetricDifference all return a new Set, leaving the originals untouched).

A practical example: comparing permission sets

This is the kind of code that used to require the manual helpers above, and now reads almost like the requirement itself:

function getPermissionChanges(currentPermissions, requestedPermissions) {
  return {
    added: requestedPermissions.difference(currentPermissions),
    removed: currentPermissions.difference(requestedPermissions),
    unchanged: currentPermissions.intersection(requestedPermissions),
  };
}

const current = new Set(["read", "write"]);
const requested = new Set(["write", "delete"]);

getPermissionChanges(current, requested);
// { added: Set {'delete'}, removed: Set {'read'}, unchanged: Set {'write'} }

Method reference

Method Returns
a.union(b) Elements in a or b (or both)
a.intersection(b) Elements in both a and b
a.difference(b) Elements in a but not in b
a.symmetricDifference(b) Elements in exactly one of a, b
a.isSubsetOf(b) true if every element of a is in b
a.isSupersetOf(b) true if every element of b is in a
a.isDisjointFrom(b) true if a and b share no elements

💡 Consiglio

All seven methods accept any “set-like” object as their argument, not just a real Set — an object with a size property and has()/keys() methods works too, which is useful if you’re interoperating with a library that models its own collection type.

FAQ

❓ Do these methods mutate the original sets?

No — union, intersection, difference, and symmetricDifference all return a brand-new Set. The three predicate methods (isSubsetOf, isSupersetOf, isDisjointFrom) return a boolean and don’t touch either set at all.

❓ Is this faster than the manual array-based version?

Generally yes — engines can implement these operations with better than the naive O(n × m) complexity a filter+includes combination often has, especially for large sets. Don’t micro-optimize around it, but it’s a reasonable default now instead of hand-rolled loops.

❓ Do I still need a library like lodash's set utilities?

For these specific operations, no — native Set methods cover the common cases directly. Libraries remain useful for things natively unsupported, like ordered set operations preserving a specific sort order across the result.

Conclusion

Native Set operations replace a class of hand-rolled utility function that nearly every JavaScript codebase eventually accumulates. union, intersection, difference, symmetricDifference, and the three subset/superset/disjoint predicates cover the vast majority of real-world set comparisons — reach for them directly instead of writing filter+includes helpers from scratch.

References

Condividi:XLinkedInFacebookWhatsApp
Diego Betto

Written by

Diego Betto

Co-Founder & CTO at PAPION. Senior full-stack engineer specializing in React, TypeScript, Node.js, and application security.