# Map and Set in JavaScript

Map and Set in JavaScript are modern data structures introduced in ES6 to solve common problems developers face when working with objects and arrays.

While objects and arrays are powerful, they have limitations when it comes to handling key-value pairs and unique data. That’s where `Map` and `Set` come in—they provide cleaner, more efficient ways to manage data.

They're not replacements for objects and arrays, they're specialized tools that do certain jobs significantly better.

This article will show you exactly what those jobs are.

## 1\. What Map Is ?

A Map is a collection of **key-value pairs**, just like an object.

The critical difference: **Map accepts any value as a key.** Numbers, objects, booleans, even other Maps. Objects only support strings and symbols as keys.

**Creating and using a Map:**

```javascript
let userRoles = new Map();

userRoles.set("priya", "admin");
userRoles.set("rahul", "editor");
userRoles.set("ananya", "viewer");

console.log(userRoles.get("priya"));   // "admin"
console.log(userRoles.has("rahul"));   // true
console.log(userRoles.size);           // 3

userRoles.delete("ananya");
console.log(userRoles.size);           // 2
```

Notice the clean API : `.set()`, `.get()`, `.has()`, `.delete()`, `.size`. Everything is explicit and purpose-built, unlike objects where you use `obj.key`, `delete obj.key`, and `Object.keys(obj).length`.

## 2\. What Set Is ?

A Set is a collection of **unique values**. Add the same value twice, it only appears once. That's the defining characteristic, and it's the whole reason Set exists.

```js
let tags = new Set();

tags.add("javascript");
tags.add("frontend");
tags.add("javascript");  // duplicate — silently ignored
tags.add("es6");

console.log(tags.size);  // 3 — not 4
console.log(tags);       // Set { "javascript", "frontend", "es6" }
```

No error on duplicates. No fuss. They're just ignored.

**Common Set operations:**

```javascript
let skills = new Set(["html", "css", "javascript", "css", "html"]);

console.log(skills.size);              // 3 — duplicates removed automatically
console.log(skills.has("javascript")); // true
console.log(skills.has("python"));     // false

skills.add("react");
skills.delete("html");

console.log([...skills]);  // ["css", "javascript", "react"]
```

**The most practical use : removing duplicates from an array:**

```javascript
let rawTags = ["javascript", "css", "javascript", "react", "css", "html"];

let uniqueTags = [...new Set(rawTags)];

console.log(uniqueTags);
// ["javascript", "css", "react", "html"]
```

One line. No loops, no filters, no manual checking. Create a Set (duplicates removed automatically), spread it back into an array.

**Iterating a Set:**

```javascript
let categories = new Set(["books", "electronics", "clothing"]);

for (let category of categories) {
  console.log(category);
}
// books
// electronics
// clothing
```

## 3\. Map vs Object

Both store key-value pairs. Here's where they diverge in ways that actually matter:

**Key types:**

```javascript
// Object — keys are always strings (or symbols)
let obj = {};
obj[1] = "one";
obj[true] = "yes";

console.log(Object.keys(obj));  // ["1", "true"] — converted to strings

// Map — keys keep their actual type
let map = new Map();
map.set(1, "one");
map.set(true, "yes");

console.log([...map.keys()]);  // [1, true] — kept as number and boolean
```

**Size:**

```javascript
let obj = { a: 1, b: 2, c: 3 };
let map = new Map([["a", 1], ["b", 2], ["c", 3]]);

// Object — no built-in size
console.log(Object.keys(obj).length);  // 3 — manual calculation

// Map — built-in
console.log(map.size);  // 3 — direct property
```

**Iteration:**

```javascript
// Object — needs Object.entries(), not directly iterable
for (let [key, value] of Object.entries(obj)) {
  console.log(key, value);
}

// Map — directly iterable
for (let [key, value] of map) {
  console.log(key, value);
}
```

**Prototype pollution risk:**

Plain objects inherit from `Object.prototype`. This means they come with built-in properties like `toString`, `hasOwnProperty`, and `constructor`. These can cause subtle bugs if you're not careful with key names:

```javascript
let permissions = {};
permissions["admin"] = true;
permissions["toString"] = true;  // shadows Object.prototype.toString

// Now toString is broken on this object
console.log(permissions.toString());  // "true" — not the function anymore
```

Map has no such issue. It has no inherited prototype properties that could conflict with your data.

### **Side-by-side comparison:**

|  | Object | Map |
| --- | --- | --- |
| Key types | Strings / Symbols only | Any value |
| Size | Manual (`Object.keys().length`) | `.size` property |
| Iteration | Via `Object.entries()` | Directly iterable |
| Insertion order | Not guaranteed (older JS) | Always preserved |
| Prototype risk | Yes | No |
| Best for | Structured records, config | Dynamic key-value storage |

## 4\. Set vs Array

Both hold collections of values. The difference is purpose, arrays are ordered lists that allow duplicates; Sets are collections that guarantee uniqueness.

**Duplicate behavior:**

```javascript
let arr = [1, 2, 2, 3, 3, 3];
console.log(arr.length);  // 6 — keeps duplicates

let set = new Set([1, 2, 2, 3, 3, 3]);
console.log(set.size);    // 3 — removes duplicates
```

**Membership checking:**

```javascript
let arr = ["apple", "banana", "mango"];
let set = new Set(["apple", "banana", "mango"]);

// Array — scans the whole array O(n)
console.log(arr.includes("mango"));  // true

// Set — direct lookup O(1)
console.log(set.has("mango"));       // true
```

For large collections, this performance difference is significant. If you're frequently checking whether a value exists, Set is dramatically faster than an array.

**What arrays can do that Set can't:**

```javascript
let arr = [1, 2, 3, 4, 5];

// Index access
console.log(arr[2]);  // 3

// map, filter, reduce
let doubled = arr.map(x => x * 2);

// Duplicates (when you want them)
let repeated = [1, 1, 2, 2, 3];
```

Sets don't support index access `set[0]` gives you `undefined`. They also don't have `map`, `filter`, or `reduce` directly. For those operations, convert to an array first:

```javascript
let set = new Set([1, 2, 3, 4, 5]);
let doubled = [...set].map(x => x * 2);
// [2, 4, 6, 8, 10]
```

### **Side-by-side comparison:**

|  | Array | Set |
| --- | --- | --- |
| Duplicates | Allowed | Not allowed |
| Index access | Yes (`arr[0]`) | No |
| Has `.map/.filter` | Yes | No (convert first) |
| Membership check | `includes()` — O(n) | `has()` — O(1) |
| Order preserved | Yes | Yes |
| Best for | Ordered lists, transformations | Unique collections, lookup |

## 5\. When to Use Map and Set

The clearest signal for reaching for Map or Set is when you find yourself working around a limitation of plain objects or arrays. Use Map when: You need non-string keys, especially when you want to associate data with DOM elements or objects:

```javascript
let elementData = new Map();

let button = document.getElementById("submit");
let input  = document.getElementById("email");

elementData.set(button, { clickCount: 0, lastClicked: null });
elementData.set(input,  { errorCount: 0, lastError: null });
```

You need to know the size directly, or iterate keys and values frequently:

```javascript
let config = new Map([
  ["theme", "dark"],
  ["language", "en"],
  ["timezone", "IST"]
]);

console.log(`${config.size} settings loaded`);

for (let [key, value] of config) {
  applyConfig(key, value);
}
```

The data is highly dynamic, keys being added and removed frequently:

```javascript
let activeSessions = new Map();

function login(userId, token) {
  activeSessions.set(userId, { token, loginTime: Date.now() });
}

function logout(userId) {
  activeSessions.delete(userId);
}

function isActive(userId) {
  return activeSessions.has(userId);
}
```

**Use Set when:**

You're collecting values and don't want duplicates:

```javascript
// Collecting unique error codes from multiple API calls
let errorCodes = new Set();

responses.forEach(response => {
  if (response.error) errorCodes.add(response.error.code);
});

console.log([...errorCodes]);  // only unique error codes
```

You need fast membership checking over a large list:

```javascript
// Blocklist lookup — Set is much faster than array for this
let blockedUsers = new Set(["user_42", "user_99", "user_187"]);

function canPost(userId) {
  return !blockedUsers.has(userId);
}
```

You want to find the union, intersection, or difference of two collections:

```js
let setA = new Set([1, 2, 3, 4]);
let setB = new Set([3, 4, 5, 6]);

// Union — all values from both
let union = new Set([...setA, ...setB]);
// Set { 1, 2, 3, 4, 5, 6 }

// Intersection — only values in both
let intersection = new Set([...setA].filter(x => setB.has(x)));
// Set { 3, 4 }

// Difference — values in A but not B
let difference = new Set([...setA].filter(x => !setB.has(x)));
// Set { 1, 2 }
```

These patterns come up in real features, finding common friends, shared tags, users who belong to one group but not another.

## Conclusion

Map and Set aren't replacements for objects and arrays. They're specialized tools that solve specific problems cleanly.

Reach for **Map** when your keys aren't strings, when you need reliable size and iteration, or when data is dynamic enough that an object's prototype quirks could be a problem.

Reach for **Set** when uniqueness matters, when you need fast membership checking, or when you want to perform set operations like union and intersection cleanly.
