Set
A Set is a collection of unique values. Add the same value twice and it's stored once. Membership tests are fast — much faster than array.includes on a large array — and the iteration order is the order you added things. When you find yourself writing if (!arr.includes(x)) arr.push(x), you probably want a Set.
Creating a Set
const empty = new Set();
const fromArray = new Set([1, 2, 3, 2, 1]); // duplicates dropped
console.log(fromArray); // Set(3) { 1, 2, 3 }
const fromString = new Set("hello");
console.log(fromString); // Set(4) { 'h', 'e', 'l', 'o' }Set(3) { 1, 2, 3 }
Set(4) { 'h', 'e', 'l', 'o' }Any iterable works as the constructor argument: arrays, strings, other Sets, generators, iterators. Duplicates are filtered as values are added.
add, has, delete, size
The core methods are all O(1) (constant time) on average:
set.add(value)— adds, returns the set so you can chain.set.has(value)— true/false membership check.set.delete(value)— removes; returns true if it was there.set.clear()— empties the set.set.size— number of values (a property, not a method).
const tags = new Set();
tags.add("javascript").add("web").add("javascript");
console.log(tags.size); // 2
console.log(tags.has("web")); // true
tags.delete("web");
console.log(tags.has("web")); // false2 true false
Equality: SameValueZero
Sets compare values using the SameValueZero algorithm. In practice that means:
NaNequals itself (unlike===) — so a Set holds at most oneNaN.+0and-0are treated as equal — one zero only.Objects are compared by reference, never structurally. Two different object literals with the same shape are different values.
const s = new Set();
s.add(NaN).add(NaN); // only one NaN stored
console.log(s.size); // 1
s.add({ id: 1 }).add({ id: 1 }); // two different objects
console.log(s.size); // 3 — NaN + two object referencesIteration order
Sets iterate in insertion order — the order values were first added. This is guaranteed by the spec, which is why Set is also handy as a dedup-while-preserving-order primitive.
const order = new Set();
order.add("c").add("a").add("b").add("a");
for (const v of order) console.log(v);c a b
Re-adding an existing value doesn't reorder it. To "move" a value to the end, delete then add.
Iterating
A Set is iterable, so for...of and spread work directly. It also exposes values(), keys() (an alias of values()) and entries() (which yields [value, value] pairs, for API compatibility with Map).
const nums = new Set([1, 2, 3]); for (const n of nums) console.log(n); nums.forEach(n => console.log(n)); const asArray = [...nums]; console.log(asArray);
1 2 3 1 2 3 [ 1, 2, 3 ]
Common idioms
A handful of one-liners come up often:
Dedup an array
const unique = [...new Set([1, 2, 2, 3, 1, 4])]; console.log(unique); // [ 1, 2, 3, 4 ]
Fast 'has any of these?' check
const allowed = new Set(["admin", "editor", "viewer"]);
function canAccess(role) {
return allowed.has(role); // O(1)
}Set algebra by hand
const a = new Set([1, 2, 3]); const b = new Set([2, 3, 4]); 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))); console.log([...union]); // [ 1, 2, 3, 4 ] console.log([...intersection]); // [ 2, 3 ] console.log([...difference]); // [ 1 ]
When to choose Set over array
Uniqueness matters — you want to store something at most once.
You need frequent
haschecks. On large collections,Set.hasis dramatically faster thanArray.includes.Insertion order matters but indexes do not. Sets remember insertion order; they do not let you index by position.
Reach for an array when you need ordered indexing, sorting, or the rich map/filter/reduce API. You can always do [...set] to switch.
What Set is not
Not a way to dedup structurally:
new Set([{a:1}, {a:1}])has size 2 because each object is a different reference.Not sorted: insertion order is preserved, not numeric or alphabetical order.
Not JSON-serialisable directly:
JSON.stringify(new Set([1,2]))produces"{}". Convert with[...set]first.