Learn by reading through in order

Map and Set — Removing Duplicates and Keyed Tallies

Learn Set, which holds each value only once, and Map, which keeps key-value pairs in insertion order.

Collect the tags attached to a product into an array, and the same tag keeps turning up — knowing how many distinct ones there actually are means checking with includes every time. Wanting to hold a key-value pair, like a stock count keyed by product ID, hits the same wall with plain arrays: you'd need two separate arrays for IDs and quantities lined up by position, and an object's keys are limited to strings and symbol, so a number can't be used as a key as-is.

This article covers Set, which never holds duplicates, and Map, which can use more than just strings as keys.

Removing Duplicates — Building a Set

Say you want to know how many distinct tags are actually in use across a list of tagged products. In a plain array, the same tag shows up more than once, so counting it as-is inflates the total. You need a data structure that holds duplicates removed from the start.

A Set (a data structure holding each value only once) is built by passing an array, as in new Set(tags). The new at the front is syntax you put before creating a new instance of a fixed kind of data structure, like Set or Map.

Since the same value never adds a duplicate, reading size alone tells you the number of distinct values. Whether two values count as the same follows the same rule as ===.

A Set isn't an array, so an index like uniqueTags[0] won't read anything back. When you need it as an array, convert it with spread syntax, as in [...uniqueTags].

Order stays exactly as items were first added, so the original array's order of first appearance carries through unchanged.

A Set Merges Duplicate Values Into One
1stSaleDoesn'thave it yetGoes inas the 1st2ndNewDoesn'thave it yetGoes inas the 2nd3rdSaleAlreadyhas itCount doesn'tincrease
Nothing happens when a value it already has comes in. The order items were added stays exactly as it was.
const tags = ["Sale", "New", "Sale", "Free Shipping"];

// Pass an array, and you get a Set with duplicates removed
const uniqueTags = new Set(tags);
console.log(uniqueTags.size);              // 3

// Convert back to an array with spread syntax (order of addition preserved)
console.log([...uniqueTags].join(", "));   // Sale, New, Free Shipping

// The original array is untouched
console.log(tags.length);                  // 4

// You can also start from an empty Set
const empty = new Set();
console.log(empty.size);                   // 0

Pull out the distinct tags actually in use, from an array of tags attached to products. tags is already declared.

① Display the original array's count.

② Build a data structure with duplicates removed, and display its count.

③ Convert the deduplicated result back to an array, joined with ", ", and display it.

④ Display the 2nd item of ③'s array, confirming that order was preserved.

(If it runs correctly, an explanation will appear.)

JavaScript / TypeScript Editor

Run code to see output

Adding and Removing Values — add, has, and delete

Sometimes you want to log visitor IDs and count the same person as 1 no matter how many times they come back. Keep this in an array, and every addition means checking with includes before push — the more records pile up, the longer that check takes.

A Set has add for adding a value, has for checking whether it has one, delete for removing one, and size for the count. add does nothing when the same value comes in, so you never need to check before adding.

has returns true or false, and delete returns whether it actually removed something.

How size Moves with add and delete
add a new IDDoesn'thave it yetsize goesup by 1add thesame IDAlreadyhas itsize staysthe samedeletean IDRemoves itfrom the recordsize goesdown by 1
adding the same value any number of times never grows the count. Only removing shrinks it.
const visitors = new Set(["u-102", "u-337"]);

// Check whether it has one
console.log(visitors.has("u-102"));   // true
console.log(visitors.has("u-999"));   // false

// Add one. The same value doesn't change the count
visitors.add("u-541");
console.log(visitors.size);           // 3
visitors.add("u-541");
console.log(visitors.size);           // 3

// Remove one. Whether it actually removed something comes back
console.log(visitors.delete("u-102"));  // true
console.log(visitors.delete("u-102"));  // false (already gone)
console.log(visitors.size);             // 2

Operate on a record of today's visitors and confirm how the count changes. visitors is already declared.

① Display whether u-337 has already been recorded.

② Record u-999, and display the count.

③ Record the same u-999 again, and display the count.

④ Remove u-102 from the record, and display whether it's still there.

JavaScript / TypeScript Editor

Run code to see output

Holding Key-Value Pairs — Map

Sometimes you want to hold a key and value as a pair, like a stock count keyed by product ID. An object can do this too, but for tallying keys that are decided at runtime — like IDs — and adjusting them over time, a data structure with dedicated methods is better suited.

A Map (a data structure holding key-value pairs in the order they were registered) is built with new Map(), and you register entries with set(key, value). To read one, use get(key); to check whether one exists, has(key); for the count, size.

set the same key again, and the value gets overwritten. To start with entries already filled in, pass an array of pairs, as in new Map([["P-001", 12], ["P-002", 5]]).

A Map Gets and Sets Pairs by Key
set P-001and 12Registersit as a pairsize goesup by 1get, givingP-001Looks upthe keyReturns thevalue, 12get, givingP-999Not foundReturnsundefined
set registers a pair, and get looks up a value by key. A key with no entry returns undefined.
const stock = new Map();

// Register a pair with set
stock.set("P-001", 12);
stock.set("P-002", 5);
console.log(stock.size);            // 2

// Look up a value from a key with get
console.log(stock.get("P-002"));    // 5
console.log(stock.get("P-999"));    // undefined
console.log(stock.has("P-999"));    // false

// set the same key again, and it overwrites
stock.set("P-001", 8);
console.log(stock.get("P-001"));    // 8
console.log(stock.size);            // 2 (the count doesn't grow)

// You can also build it with pairs already filled in
const prices = new Map([["P-001", 3980], ["P-002", 2480]]);
console.log(prices.get("P-001"));   // 3980

A Map's Values Can't Be Read with Dot Notation

Write stock["P-001"] instead of stock.get("P-001"), and you won't get the value back. That syntax looks for a property on the Map object itself, which has nothing to do with the pairs you registered, so it returns undefined. Route reading and writing through get and set.

Build a data structure holding stock counts keyed by product ID. stock is already declared, empty.

① Register P-001 as 12, P-002 as 5, and P-003 as 3, then display the count.

② Display P-002's stock count.

③ Display the stock count for the unregistered P-999.

④ Update P-001's stock to 4, and display the value after updating.

JavaScript / TypeScript Editor

Run code to see output

Choosing Between Map and Object — Key Type and Order

Since it holds keys and values just like an object does, you choose between them. When the keys are fixed at the time you write the code, like settings fields, an object works; when you're tallying keys that grow at runtime, like IDs or dates, Map is easier to work with.

Key type and ordering are what you base that decision on.

An object's keys get converted to strings, so even writing record[2026], the actual key is "2026". A Map uses whatever value you pass as the key exactly as it is, so the number 2026 and the string "2026" are treated as different keys.

Order works differently too — a Map always keeps the order things were registered in.

A Map Keeps a Key's Type Exactly As It Is
Register withMap using 2026Stays anumber keyget(2026)retrieves itLook up thesame Map by stringTreated as adifferent keyReturnsundefinedRegister withObject using 2026Converted toa stringA string alsoretrieves it
A Map treats the number 2026 and the string "2026" as different keys. An object converts them both to strings.
AspectObjectMap
Types allowed as keysConverted to stringsAny type, kept as-is
OrderingInteger-like keys sort firstStays in registration order
Counting entriesObject.keys' lengthsize
Reading & writingobj.key / obj[key]get and set
Best suited forA fixed collection of fieldsTallying keys that grow at runtime

To list out a Map's contents, write [...stock.entries()] to convert it into an array of [key, value] pairs, each a 2-element array. That lets you pull out one pair at a time, as in entries[0]. Repeating the same step once per pair, however many there are, is covered again in the article on loops.

When you need just the keys, use stock.keys() — but since what it returns isn't an array, you can't chain join onto it directly. Write [...stock.keys()] to convert it to an array first.

const scores = new Map([["math", 80]]);

// A Map keeps a number key as a number
scores.set(2026, "this year's tally");
console.log(scores.get(2026));               // this year's tally
console.log(scores.get("2026"));             // undefined (a different key)

// An object's keys get converted to strings
const record = {};
record[2026] = "this year's tally";
console.log(Object.keys(record)[0]);         // 2026
console.log(typeof Object.keys(record)[0]);  // string
console.log(record["2026"]);                 // this year's tally (treated as the same key)

// Convert keys() / entries() to an array with spread syntax before using them
console.log([...scores.keys()].join(", "));   // math, 2026

const pairs = [...scores.entries()];
console.log(pairs[0].join(": "));             // math: 80

// An object sorts integer-like keys first
const ranking = {};
ranking[3] = "c";
ranking[1] = "a";
console.log(Object.keys(ranking).join(", "));  // 1, 3

List a stock Map's contents, then confirm the difference key type makes. stock is already declared, with 3 pairs registered.

① Turn just the registered keys into an array, and display them joined with ", ".

② Convert the 3 pairs into an array of [key, value] pairs, and display each one formatted as "key: value" across 3 lines.

③ Register "Annual Ranking" with the number 2026 as the key, and retrieve it with that same key to display it.

④ Retrieve the same entry with the string "2026" instead, and display what comes back.

JavaScript / TypeScript Editor

Run code to see output
QUIZ

Knowledge Check

Answer each question one by one.

Q1With const ids = new Set(["u-1", "u-2"]);, run ids.add("u-1");. What's ids.size?

Q2With stock as a Map, what does stock.get("P-999") return for an unregistered key?

Q3Register the number 2026 as a key. What's different between Map and an object?