Skip to main content

Command Palette

Search for a command to run...

Map and Set in JavaScript: Beyond Objects and Arrays

Updated
8 min readView as Markdown

When you first learn JavaScript, you often reach for objects to store key‑value data and arrays to store lists. But as your applications grow, you start to hit limits: objects aren’t truly designed for flexible keys, and arrays aren’t built for enforcing uniqueness. That’s where Map and Set come in.

In this post we’ll cover:

  • What Map is and how it differs from plain objects

  • What Set is and its uniqueness property

  • How Map compares to key‑value storage in objects

  • How Set compares to arrays

  • When to use Map vs Set (and when to stick with objects/arrays)

  • And the problems with plain objects and arrays that Map/Set solve

We’ll keep it practical, but go a bit deeper into the underlying behavior you should understand as a working developer.

What Map Is

A Map is a collection that stores key‑value pairs where:

  • Keys can be any type (strings, numbers, booleans, objects, functions, etc.).

  • Each key is unique within the map; if you set the same key again, it overrides the old value.

  • Keys are compared by identity for objects and functions, not by their contents.

const cache = new Map();

cache.set("name", "Alice");
cache.set(100, "score");
cache.set({ id: 1 }, "object key"); // object as key

console.log(cache.get("name")); // "Alice"
console.log(cache.has("name")); // true
console.log(cache.size);        // 3

A Map is ordered: entries are stored in insertion order, and you can iterate over them with for...of, keys(), values(), or entries().

Why Map Was Needed

Plain objects can store key‑value data, but they have limitations:

  • Keys are effectively strings (or symbols).

    • obj[1] and obj["1"] point to the same property.

    • Any non‑string key is coerced to a string.

  • Prototypes can leak in:

    • If obj inherits from a constructor, obj may have unexpected keys from its prototype.
  • No built‑in methods for iteration or size:

    • You must use Object.keys() / Object.values() and count manually.

    • Prototypical keys can leak into for...in.

A Map solves these by:

  • Treating every key as‑is (no coercion).

  • Providing reliable size and iteration APIs.

  • Avoiding prototype pollution concerns.

In essence, Map is a more principled key‑value structure for when you want to treat keys as real, arbitrary values rather than coerced strings.

What Set Is

A Set is a collection that stores unique values of any type. Each value appears at most once in a set.

const tags = new Set();

tags.add("javascript");
tags.add("nodejs");
tags.add("javascript"); // ignored, already exists

console.log(tags.size); // 2
console.log(tags.has("nodejs")); // true

// Iterate
for (const tag of tags) {
  console.log(tag); // "javascript", "nodejs"
}

Like Map, Set:

  • Preserves insertion order when iterating.

  • Works with any value type (primitives or objects).

  • Provides clean methods (add, has, delete, size, clear, forEach).

Uniqueness Property

The key feature of Set is uniqueness:

  • For primitive values, equality is straightforward: "a" === "a", 1 === 1.

  • For objects and functions, equality is by identity (the same reference).

    • Two different objects { x: 1 } and { x: 1 } are not equal in a Set.
const set = new Set();
const a = { x: 1 };
const b = { x: 1 };

set.add(a);
set.add(b);
set.add(a); // a is already in, so this does nothing

console.log(set.size); // 2 (a and b are different objects)

This uniqueness is useful for:

  • De‑duplicating arrays.

  • Tracking seen items (e.g., visited nodes, cached IDs).

  • Building “has‑this‑been‑seen” logic.

For example, remove duplicates from an array:

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

Here, Set enforces uniqueness and ... spreads the values back into an array.

Difference Between Map and Plain Objects

Feature Map Plain Object ({})
Keys Any type; no coercion Effectively strings (or symbols)
Size tracking map.size is built‑in Object.keys(obj).length is manual and fragile
Iteration Designed for iteration (for...of, keys, values) for...in, Object.keys, etc.; can leak prototype
Key-value semantics Truly key‑value store; no “methods” mix in Properties can be data or methods on prototype
Performance (many keys) Often better for frequent key lookups/updates Can degenerate if keys are abused as data
Use case Generic key‑value structure, caching, LRU, etc. Config objects, DTOs, module exports, etc.

In practice, treat Map as the “data” key‑value construct and objects as “configuration” or “domain object” constructs. Use Map when you care about:

  • Flexible keys (e.g., objects as keys for caching by reference).

  • Clean iteration and size tracking.

  • Avoiding prototype pitfalls.

Use plain objects when you want:

  • Simple, readable config objects.

  • JSON‑serializable structures.

  • Class‑style methods via prototypes.

Difference Between Set and Arrays

Feature Set Array
Uniqueness Values are unique; no duplicates Values can repeat
Indexing No numeric index access (no set[0]) Full index access (arr[0], arr.length)
Insertion order Yes, but not directly indexable Yes, with numeric indices
Built‑in methods add, has, delete, size push, pop, shift, slice, etc.
Primary purpose Store unique values; fast membership checks Ordered list with random access and iteration
Duplicates Ignored Allowed and common

Set excels at:

  • Fast membership checks (has(value)).

  • Automatic de‑duplication.

  • Tracking sets of things (e.g., IDs, tags, seen items).

Array excels at:

  • Accessing by index (arr[i]).

  • Random insertion/removal.

  • Numerical ordering and slicing.

A common pattern: use Set for uniqueness logic, then convert to Array when you need indices or JSON‑serialization:

const ids = [1, 2, 2, 3, 4, 4];
const uniqueIds = [...new Set(ids)]; // [1, 2, 3, 4]

When to Use Map and Set

Use Map when:

  • You need key‑value storage with non‑string keys (e.g., objects, functions).

  • You want reliable, fast key lookups (map.get(key)).

  • You care about insertion order and clean iteration (for...of map).

  • You’re building:

    • Caches (e.g., cache.set(someObj, data)).

    • LRU / memoization stores.

    • Graph‑style structures (e.g., node‑to‑neighbors maps).

Example: caching template functions by reference:

const templateCache = new Map();

function getTemplate(fn) {
  if (!templateCache.has(fn)) {
    const tmpl = compileTemplate(fn.source);
    templateCache.set(fn, tmpl);
  }
  return templateCache.get(fn);
}

Use Set when:

  • You need unique values and don’t care about indices.

  • You want fast has checks (set.has(value)).

  • You’re de‑duplicating an array or filtering duplicates.

  • You’re tracking seen items (e.g., visited = new Set() in graph traversal).

Example: tracking seen user IDs efficiently:

const visited = new Set();

function processUser(id) {
  if (visited.has(id)) return; // skip if already processed
  visited.add(id);
  // process logic...
}

Stick with objects and arrays when:

  • You want simple JSON‑serializable configs ({ name: "Alice", role: "admin" }).

  • You need index‑based random access (arr[42]).

  • You’re not worried about key uniqueness or prototype interference.

Objects and arrays are still the default for general‑purpose data; Map and Set are the specialized tools for specific jobs.

Problems with Traditional Objects and Arrays

Problems with Plain Objects as Key‑Value Stores

  1. Key coercion:

    • obj[1] and obj["1"] are the same, which can be confusing.

    • obj[true] and obj["true"] collide.

  2. Prototypes leak:

    • for...in may iterate over inherited properties, not just data.

    • obj.toString might conflict with your data key.

  3. No built‑in size:

    • Counting keys requires Object.keys(obj).length.

    • This is fragile if you’re not careful about prototypes.

Problems with Arrays for Uniqueness

  1. Duplicates are allowed:

    • Arrays don’t prevent arr.push("a") twice.
  2. has checks are slow:

    • Checking arr.includes(value) is O(n) vs Set.has(value) which is typically O(1).
  3. Unintended mutation:

    • Manual de‑duplication logic (Array.from(new Set(arr))) is cleaner and more predictable.

Map and Set solve these by providing clear, dedicated APIs for key‑value storage and uniqueness, respectively.

Wrapping Up

  • Map is a flexible, ordered key‑value store that works with any key type and avoids the quirks of plain objects.

  • Set is a collection that guarantees uniqueness and provides fast membership checks, ideal for de‑duplicating and tracking seen items.

Use Map when you need true key‑value semantics and Set when you need unique values. Reach for Map/Set when you hit the limitations of objects and arrays—coerced keys, prototype leaks, slow uniqueness checks, or messy duplicate‑handling code.

By understanding these distinctions, you’ll write cleaner, more efficient JavaScript that’s better suited for real‑world applications and interview‑style problems alike.