ES6 introduced Map and Set as first-class collection types, plus WeakMap and WeakSet for memory-sensitive associations. Each solves problems that plain objects and arrays handle awkwardly.

Set — Unique Values

A Set stores unique values of any type. Duplicates are silently ignored.

  const tags = new Set(['js', 'web', 'js']);
console.log(tags.size); // 2

tags.add('node');
tags.add('js');         // duplicate — ignored
tags.has('web');        // true
tags.delete('web');
tags.clear();           // remove all

// Iterate
for (const tag of tags) {
    console.log(tag);
}

// Set operations
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
const union = new Set([...a, ...b]);           // {1, 2, 3, 4}
const intersection = new Set([...a].filter(x => b.has(x))); // {2, 3}
  

Remove Duplicates from Arrays

  const numbers = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(numbers)]; // [1, 2, 3]

// Unique objects by property (Set can't dedupe objects by field)
const users = [{ id: 1 }, { id: 2 }, { id: 1 }];
const uniqueUsers = [...new Map(users.map(u => [u.id, u])).values()];
  

Map — Key-Value Pairs with Any Key Type

A Map stores key-value pairs where keys can be any type — objects, functions, symbols, or primitives.

  const userRoles = new Map();
userRoles.set('alice', 'admin');
userRoles.set('bob', 'user');

console.log(userRoles.get('alice')); // 'admin'
console.log(userRoles.size);         // 2

// Object as key
const sessionKey = { userId: 42 };
userRoles.set(sessionKey, { token: 'abc123' });
console.log(userRoles.get(sessionKey)); // { token: 'abc123' }

// Iterate
for (const [key, value] of userRoles) {
    console.log(key, value);
}

userRoles.forEach((value, key) => {
    console.log(key, '→', value);
});
  

Map vs Object

Feature Map Object
Key types Any String or Symbol
Size .size property Manual Object.keys().length
Iteration order Guaranteed insertion order Mostly insertion order (integer keys reorder)
Prototype keys None by default Inherits from Object.prototype
JSON serialization Not directly serializable Native JSON.stringify support
Performance Better for frequent add/delete Better for static record-like data

Map from Object

  const obj = { a: 1, b: 2 };
const map = new Map(Object.entries(obj));
const backToObj = Object.fromEntries(map);
  

WeakMap — Object Keys with Garbage Collection

WeakMap keys must be objects. If the key object is no longer referenced elsewhere, the entry is automatically garbage-collected.

  const privateData = new WeakMap();

class User {
    constructor(name) {
        privateData.set(this, { name, createdAt: Date.now() });
    }
    getName() {
        return privateData.get(this).name;
    }
}

const user = new User('Alice');
console.log(user.getName()); // 'Alice'
// When user is dereferenced, privateData entry is collected
  

WeakMap has no .size, no iteration, and no way to list keys — by design.

WeakSet — Weak Object References

WeakSet stores objects only. Useful for tracking visited nodes without preventing garbage collection:

  const visited = new WeakSet();

function traverse(node) {
    if (visited.has(node)) return;
    visited.add(node);
    // process node...
    node.children?.forEach(traverse);
}
  

Practical Examples

Count Word Frequencies with Map

  function countWords(text) {
    const counts = new Map();
    for (const word of text.toLowerCase().split(/\W+/)) {
        if (!word) continue;
        counts.set(word, (counts.get(word) || 0) + 1);
    }
    return counts;
}

countWords("hello world hello"); // Map { 'hello' => 2, 'world' => 1 }
  

Cache Expensive Computations

  const cache = new Map();

function fibonacci(n) {
    if (cache.has(n)) return cache.get(n);
    if (n <= 1) return n;
    const result = fibonacci(n - 1) + fibonacci(n - 2);
    cache.set(n, result);
    return result;
}
  

For object-keyed caches where memory matters, use WeakMap instead.

Track Event Listeners for Cleanup

  const listeners = new WeakMap();

function addManagedListener(element, event, handler) {
    element.addEventListener(event, handler);
    if (!listeners.has(element)) {
        listeners.set(element, []);
    }
    listeners.get(element).push({ event, handler });
}
  

Best Practices

  • Use Set when you need uniqueness testing with O(1) lookup.
  • Use Map when keys are non-strings or you need guaranteed iteration order.
  • Use WeakMap/WeakSet for metadata attached to objects without memory leaks.
  • Prefer plain objects for JSON-serializable record structures with string keys.
  • Convert Map to array for serialization: [...map.entries()].

Troubleshooting

Issue Cause Fix
Set doesn’t dedupe objects Compared by reference, not value Use Map keyed by a unique property
map.get(objKey) returns undefined Different object reference as key Store and reuse the same reference
Map not in JSON.stringify Maps aren’t JSON-native Convert with Object.fromEntries(map)
WeakMap key error Primitive key used Keys must be objects
Unexpected iteration order on Object Integer-like keys sort first Use Map for insertion-order guarantees

Set for unique values, Map for flexible key-value lookups, WeakMap/WeakSet for object-associated data that should not prevent garbage collection.