Set Toolbox 0/8

JavaScript basicsSets in plain wordsOne tool at a time

Set
Toolbox

A Set is a collection where every value appears only once, like a guest list that ignores repeat names. This guide shows how to fill one, remove duplicates, loop through it and combine sets with union and intersection, with a small example for each that you can run.

JavaScript ES202516 tools
01

Make and read

Create a Set, add values, and check whether a value is in it.

Start herenew SetaddhasMust know

A Set is like an array that refuses repeats. Add the same value twice and it keeps only one. It has no positions, so you do not ask "what is at number 2". You ask "is this value in here?", and it answers very fast.

Picture it: add

"js""node""js"new Set(...)"js""node"The repeat is dropped.
new Set(list?)

Makes an empty set, or one filled from an array or other list.

See the example
Gives a new setMakes a new one
add(value)

Adds the value if it is not already there.

See the example
Gives the same setChanges it
has(value)

Checks if the value is in the set.

See the example
Gives true or falseOnly reads
size

Counts the values. A property, so no brackets.

See the example
Gives a numberOnly reads
make.jsJAVASCRIPT
const tags = new Set();

tags.add("js");
tags.add("node").add("ts");     // add gives the set back, so you can chain
tags.add("js");                 // already there: quietly ignored

console.log(tags.size);         // 3, not 4
console.log(tags.has("node"), tags.has("go"));
console.log(tags);

Easy way to remember: add never complains about repeats, it just ignores them. That is the whole point of a Set.

Terminal OUTPUT
$ node make.js
3
true false
Set(3) { 'js', 'node', 'ts' }
02

Remove duplicates

Clean repeats out of any array in one line, and count how many different values there are.

Most useddedupe[...new Set()]
dedupe.jsJAVASCRIPT
const visits = ["home", "cart", "home", "pay", "cart", "home"];

const unique = [...new Set(visits)];     // the one line dedupe
console.log(unique);
console.log(new Set(visits).size, "different pages");

// works for letters too
console.log([...new Set("mississippi")].join(""));

// objects are compared by identity, not by what is inside
const a = { id: 1 };
console.log(new Set([a, a, { id: 1 }]).size);

Watch for this: two objects with the same contents are still two different objects, so a Set keeps both. It only drops the exact same object.

Terminal OUTPUT
$ node dedupe.js
[ 'home', 'cart', 'pay' ]
3 different pages
misp
2

This is the most common reason people reach for a Set. Spread it back with [...set] when you need an array again.

03

Change and remove

Take one value out, or empty the set completely.

Changingdeleteclear
delete(value)

Removes that value.

See the example
Gives true if it was thereChanges it
clear()

Removes every value at once.

See the example
Gives undefinedChanges it
change.jsJAVASCRIPT
const online = new Set(["asha", "ravi", "meera"]);

console.log(online.delete("ravi"));   // true: found and removed
console.log(online.delete("zoya"));   // false: was not there
console.log(online);

online.clear();                       // remove everything
console.log(online.size);

Easy way to remember: delete tells you whether it found the value, so you can use its answer in an if.

Terminal OUTPUT
$ node change.js
true
false
Set(2) { 'asha', 'meera' }
0
04

Loop through

Walk through every value in the order you added them, or turn the set into an array.

Loopingfor...offorEachspread
for (const v of set)

Visits every value, oldest first.

See the example
Gives nothingOnly reads
forEach(value, value)

Runs your helper on every value. It passes the value twice.

See the example
Gives nothingOnly reads
[...set]

Spreads into an array so you can map, filter or sort.

See the example
Gives an arrayMakes a copy
loop.jsJAVASCRIPT
const colours = new Set(["red", "amber", "violet"]);

for (const colour of colours) {       // in the order they were added
  console.log(colour);
}

// forEach gives the value twice, to match the Map shape
colours.forEach((value, sameValue) => console.log(value === sameValue));

console.log([...colours].map((c) => c.toUpperCase()));  // spread for array tools

Why it matters: a Set has no map or filter of its own. Spread it into an array first, then use the array tools you know.

Terminal OUTPUT
$ node loop.js
red
amber
violet
true
true
true
[ 'RED', 'AMBER', 'VIOLET' ]
05

Set maths

Combine two sets: everything in either, only what they share, or what one has that the other does not.

New in 2024unionintersectiondifference
MethodGives youFrontend + backend teams
a.union(b)everything in either setall four people
a.intersection(b)only what is in bothpeople on both teams
a.difference(b)in a but not in bfrontend only
a.symmetricDifference(b)in exactly one of themnot on both
a.isSubsetOf(b)true if all of a is in byes or no
a.isSupersetOf(b)true if all of b is in ayes or no
a.isDisjointFrom(b)true if they share nothingyes or no
maths.jsJAVASCRIPT
const frontend = new Set(["asha", "ravi", "meera"]);
const backend = new Set(["meera", "kiran", "asha"]);

console.log(frontend.union(backend));               // in either team
console.log(frontend.intersection(backend));        // in both teams
console.log(frontend.difference(backend));          // frontend only
console.log(frontend.symmetricDifference(backend)); // in exactly one team

const leads = new Set(["asha"]);
console.log(leads.isSubsetOf(frontend), frontend.isSupersetOf(leads));
console.log(leads.isDisjointFrom(new Set(["kiran"])));

Needs: Node 22, Chrome 122, Firefox 127 or Safari 17 and newer. Every method gives a new set and leaves both originals alone.

Terminal OUTPUT
$ node maths.js
Set(4) { 'asha', 'ravi', 'meera', 'kiran' }
Set(2) { 'asha', 'meera' }
Set(1) { 'ravi' }
Set(2) { 'ravi', 'kiran' }
true true
true
older.jsJAVASCRIPT
// the same answers on older engines, using filter
const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5]);

const union = new Set([...a, ...b]);
const both = new Set([...a].filter((x) => b.has(x)));
const onlyA = new Set([...a].filter((x) => !b.has(x)));

console.log(union, both, onlyA);

For older engines: spread and filter give the same answers. It is slower on big sets, but works everywhere.

Terminal OUTPUT
$ node older.js
Set(5) { 1, 2, 3, 4, 5 } Set(2) { 3, 4 } Set(2) { 1, 2 }
06

Set or array?

Know when a Set is the better choice than an array, and when it is not.

Choosingspeedorder
QuestionArraySet
Can values repeat?YesNo
Can I read by position?Yes list[2]No
Is "is it in here?" fast on big lists?No checks one by oneYes
Keeps the order you added?YesYes
Has map, filter, sort?YesNo spread first
Works with JSON?YesNo convert with [...set]
Best forordered lists and positionsunique values and fast lookups
07

WeakSet

Mark objects as seen or done without keeping them in memory forever.

MemoryWeakSetobjects only

A WeakSet only holds objects, and holds them loosely. Once nothing else in your program uses an object, it can drop out by itself. It has no size and no looping, just add, has and delete.

weak.jsJAVASCRIPT
// a WeakSet marks objects without keeping them alive
const processed = new WeakSet();

function handle(order) {
  if (processed.has(order)) return `order ${order.id} already done`;
  processed.add(order);
  return `processing order ${order.id}`;
}

const order = { id: 42 };
console.log(handle(order));
console.log(handle(order));          // the same object is recognised

try { processed.add("42"); } catch (err) { console.log(err.constructor.name); }
console.log(typeof processed.size);  // no size and no looping

When to reach for it: tracking which requests, orders or page elements you have already handled, without causing a memory leak.

Terminal OUTPUT
$ node weak.js
processing order 42
order 42 already done
TypeError
undefined
08

All together

Combine role permissions for a user and check what an action needs.

Putting it togetherunionisSubsetOfdifference

A person can have several roles, and each role grants a few permissions. Sets make this neat: union joins the permissions, isSubsetOf checks an action, and difference lists what is missing.

access.jsJAVASCRIPT
// who can do what: combine role permissions, then check what an action needs
const roles = {
  viewer: new Set(["read"]),
  editor: new Set(["read", "write"]),
  billing: new Set(["read", "invoice"]),
};

const permsFor = (...names) => names.reduce((all, n) => all.union(roles[n]), new Set());

const shree = permsFor("editor", "billing");
console.log([...shree]);

const can = (perms, ...needs) => new Set(needs).isSubsetOf(perms);
console.log(can(shree, "write", "invoice"));
console.log(can(permsFor("viewer"), "write"));

const missing = new Set(["read", "delete"]).difference(shree);
console.log("missing:", [...missing].join(", "));

What each part uses: new Set from module 01, union, isSubsetOf and difference from 05, and spread from 04 to print the result.

Terminal OUTPUT
$ node access.js
[ 'read', 'write', 'invoice' ]
true
false
missing: delete
TerminalBASH
# save any example as a file and run it with Node 22
$ node access.js
# or paste it into your browser console with F12

Which one do I need?

Start from what you want to do, then read across to the tool and what it gives back.

I want toUseYou get
Make a setnew Set()an empty set
Remove repeats from an array[...new Set(list)]a unique array
Add a valueset.add(v)the same set
Check a valueset.has(v)true or false
Count valuesset.sizea number
Remove a valueset.delete(v)true or false
Loop over itfor (const v of set)every value, in order
Everything in eithera.union(b)a new set
Only what both sharea.intersection(b)a new set
In a but not ba.difference(b)a new set
All of a inside b?a.isSubsetOf(b)true or false
Mark objects without leaksnew WeakSet()a weak set