Skip to content
JavaScript js collections 4 min read

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 / propertyReturnsDescription
add(value)the SetAdds value; no-op if already present
has(value)booleanWhether value is in the set
delete(value)booleanRemoves value; true if it existed
clear()undefinedRemoves all values
sizenumberCurrent 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), not length. A Set has no length, and reading it returns undefined.

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 Set whenever you need fast, repeated membership checks instead of array.includes in 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 id string) in the set if you need value-based uniqueness.
  • Use size to count, and has before delete only when you need the prior existence as a separate signal (delete already returns it).
  • Prefer the native union/intersection/difference methods on modern runtimes; keep spread/filter fallbacks for legacy targets.
  • If keys must be garbage-collected with their owning objects, consider a WeakSet instead of a Set.
Last updated June 1, 2026
Was this helpful?