# 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.

- Autore / Author: Diego Betto (https://diegobetto.com/about/)
- Pubblicato / Published: 2026-09-19
- URL: https://diegobetto.com/en/javascript-set-methods-union-intersection/
- Tag: javascript, set, 2026

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:

```js
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

```js
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:

```js
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  |

> 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**

- [Set.prototype.union() – MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/union)
- [Set.prototype.intersection() – MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/intersection)
