Set
A Set is a built-in collection that stores unique values of any type — primitives or object references. Unlike arrays, a Set automatically rejects duplicates, which makes it the natural tool for membership checks, deduplication, and lightweight set algebra. Lookups (has) run in roughly constant time rather than the linear scan you’d pay for with Array.prototype.includes, so reaching for a Set often simplifies code and speeds it up at once.
Creating a set
You construct a Set with the Set constructor. Passing an iterable (like an array) seeds it with values, silently dropping any repeats.
const empty = new Set();
const colors = new Set(["red", "green", "blue", "red"]);
console.log(colors.size); // duplicate "red" is ignored
Output:
3
Uniqueness is determined using the SameValueZero algorithm, which behaves like === with one twist: NaN is treated as equal to itself. Objects, however, are compared by reference, so two structurally identical objects are still distinct entries.
const set = new Set();
set.add(NaN).add(NaN); // only one NaN stored
set.add({}).add({}); // two different object references
console.log(set.size); // 1 NaN + 2 objects
Output:
3
Adding, checking, and removing
The core API is small and chainable. add inserts a value (and returns the set, so calls can be chained), has reports membership, delete removes a value (returning a boolean), and clear empties the set entirely.
| Method / property | Returns | Description |
|---|---|---|
add(value) | the Set | Adds value; no-op if already present |
has(value) | boolean | Whether value is in the set |
delete(value) | boolean | Removes value; true if it existed |
clear() | undefined | Removes all values |
size | number | Current count of values |
const tags = new Set();
tags.add("js").add("web").add("js"); // chained, "js" added once
console.log(tags.has("web")); // true
console.log(tags.delete("nope")); // false — wasn't there
console.log(tags.size); // 2
Output:
true
false
2
Use
size(a property), notlength. ASethas nolength, and reading it returnsundefined.
Iterating a set
A Set is iterable and preserves insertion order, so for...of walks values in the order they were first added. It also exposes keys(), values() (identical for sets), and entries() (which yields [value, value] pairs for symmetry with Map). The forEach method is available too.
const fruit = new Set(["apple", "banana", "cherry"]);
for (const item of fruit) {
console.log(item);
}
fruit.forEach((value) => console.log(value.toUpperCase()));
Output:
apple
banana
cherry
APPLE
BANANA
CHERRY
Because a Set is iterable, you can spread it into an array or hand it to anything that accepts an iterable.
const numbers = new Set([3, 1, 2]);
const asArray = [...numbers]; // [3, 1, 2]
const sum = Array.from(numbers).reduce((a, b) => a + b, 0);
console.log(sum); // 6
Output:
6
Deduplicating arrays
The single most common use of Set is collapsing duplicates out of an array. Wrap the array in a Set and spread it back out — one expression, no manual bookkeeping.
const raw = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(raw)];
console.log(unique); // [1, 2, 3, 4]
// Works for strings, and you can dedupe + transform in one pass
const words = ["Cat", "cat", "Dog"];
const lowered = [...new Set(words.map((w) => w.toLowerCase()))];
console.log(lowered); // ["cat", "dog"]
Output:
[ 1, 2, 3, 4 ]
[ 'cat', 'dog' ]
Set operations
JavaScript supports the classic mathematical set operations. Modern runtimes (Node 22+, current browsers) ship native methods — union, intersection, difference, symmetricDifference, isSubsetOf, isSupersetOf, and isDisjointFrom — that accept any set-like argument and return a new Set.
const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);
console.log([...a.union(b)]); // all members
console.log([...a.intersection(b)]); // in both
console.log([...a.difference(b)]); // in a, not b
console.log([...a.symmetricDifference(b)]); // in exactly one
console.log(new Set([1, 2]).isSubsetOf(a)); // true
Output:
[ 1, 2, 3, 4, 5, 6 ]
[ 3, 4 ]
[ 1, 2 ]
[ 1, 2, 5, 6 ]
true
If you need to support older environments, the same results are easy to compute with spreads and filters:
const union = new Set([...a, ...b]);
const intersection = new Set([...a].filter((x) => b.has(x)));
const difference = new Set([...a].filter((x) => !b.has(x)));
The native set methods are relatively new. In legacy targets, feature-detect with
typeof a.union === "function"or reach for the spread/filter fallbacks above.
Best practices
- Reach for a
Setwhenever you need fast, repeated membership checks instead ofarray.includesin a loop. - Dedupe arrays with
[...new Set(arr)]— it’s concise, fast, and order-preserving. - Remember objects are compared by reference; use a stable key (e.g. an
idstring) in the set if you need value-based uniqueness. - Use
sizeto count, andhasbeforedeleteonly when you need the prior existence as a separate signal (deletealready returns it). - Prefer the native
union/intersection/differencemethods on modern runtimes; keep spread/filter fallbacks for legacy targets. - If keys must be garbage-collected with their owning objects, consider a
WeakSetinstead of aSet.