Module: JavaScript
JavaScript·060·4 MIN READ

060: JavaScript Data Structures: Map, Set, Weak Collections, JSON, and Typed Arrays

TOPICS COVERED: JavaScript Data Structures: Map, Set, Weak Collections, JSON, and Typed Arrays

Outcomes

By the end of this lesson, you can:

  • choose between Object, Map, and Set;
  • use WeakMap and WeakSet appropriately;
  • explain why weak collections are not enumerable;
  • serialize and parse JSON safely;
  • identify JSON's data-model limitations;
  • explain the role of ArrayBuffer and typed arrays.

Map: Keyed Data with Any Key Type

Objects are excellent records. Map is excellent when the data is fundamentally a dynamic key/value collection.

js
const stockByProduct = new Map();

stockByProduct.set("tea", 10);
stockByProduct.set("coffee", 5);

console.log(stockByProduct.get("tea"));
console.log(stockByProduct.has("coffee"));
console.log(stockByProduct.size);

Keys can be objects:

js
const user = { id: 1 };
const preferences = new Map();

preferences.set(user, { theme: "dark" });

console.log(preferences.get(user));

Iterating a Map

js
for (const [key, value] of stockByProduct) {
  console.log(key, value);
}

Maps preserve insertion order.

Set: Unique Values

js
const selectedTags = new Set();

selectedTags.add("featured");
selectedTags.add("sale");
selectedTags.add("sale");

console.log(selectedTags.size); // 2

A common deduplication pattern:

js
const unique = [...new Set(["a", "b", "a"])];

Object versus Map

Use an object when you are modeling a structured entity:

js
const customer = {
  id: 1,
  name: "Maya",
  active: true,
};

Use a Map when keys are dynamic collection data:

js
const countsBySku = new Map();

Do not replace every object with a Map merely because Map has convenient methods.

WeakMap

WeakMap keys must be objects, and the collection does not keep those key objects alive by itself.

js
const metadata = new WeakMap();

let button = document.querySelector("button");

metadata.set(button, {
  clicks: 0,
});

console.log(metadata.get(button));

If nothing else references the button object, garbage collection can reclaim it; the WeakMap does not provide enumeration that would expose its lifetime.

Typical uses include private metadata and caches tied to object lifetime.

WeakSet

WeakSet stores object membership weakly.

js
const processed = new WeakSet();

function process(order) {
  if (processed.has(order)) {
    return;
  }

  processed.add(order);
  console.log("processing", order.id);
}

Structured Data with JSON

JSON supports a smaller data model than JavaScript.

js
const payload = {
  id: 101,
  name: "Tea",
  active: true,
  tags: ["hot", "drink"],
  metadata: null,
};

const json = JSON.stringify(payload);
const parsed = JSON.parse(json);

JSON Limitations

JSON does not directly preserve:

  • undefined;
  • functions;
  • Symbols;
  • BigInts;
  • Map and Set;
  • Date identity;
  • prototypes;
  • circular references.
js
JSON.stringify({
  value: undefined,
  fn() {},
}); // "{}"

BigInt throws:

js
// JSON.stringify({ id: 1n }); // TypeError

JSON Reviver and Replacer

js
const json = JSON.stringify(
  { total: 100, secret: "remove-me" },
  (key, value) => key === "secret" ? undefined : value
);

Reviver:

js
const data = JSON.parse(
  '{"createdAt":"2026-08-27T10:00:00.000Z"}',
  (key, value) => {
    if (key === "createdAt") {
      return new Date(value);
    }

    return value;
  }
);

Use these features carefully; explicit normalization functions are often easier to maintain.

Typed Arrays

Ordinary arrays can store mixed JavaScript values. Typed arrays provide numeric views over binary memory.

js
const bytes = new Uint8Array([65, 66, 67]);

console.log(bytes[0]); // 65

Underlying buffer:

js
const buffer = new ArrayBuffer(4);
const view = new Uint8Array(buffer);

view[0] = 255;

console.log(view);

Typed arrays matter for:

  • files and binary protocols;
  • graphics and audio;
  • WebAssembly;
  • network binary data;
  • performance-sensitive numeric work.

Do not use them for ordinary business arrays without a reason.

Worked Example: Inventory Index

js
function buildInventoryIndex(products) {
  const bySku = new Map();
  const categories = new Set();

  for (const product of products) {
    bySku.set(product.sku, product);
    categories.add(product.category);
  }

  return { bySku, categories };
}

const index = buildInventoryIndex([
  { sku: "BIR-1", category: "food", name: "Biryani" },
  { sku: "TEA-1", category: "drink", name: "Tea" },
  { sku: "TEA-2", category: "drink", name: "Black Tea" },
]);

console.log(index.bySku.get("TEA-1"));
console.log([...index.categories]);

Failure Example: Stringifying a Map

js
const map = new Map([["tea", 10]]);
console.log(JSON.stringify(map)); // "{}"

Normalize it explicitly:

js
const json = JSON.stringify(Object.fromEntries(map));

And restore if needed:

js
const restored = new Map(
  Object.entries(JSON.parse(json))
);

Advanced Notes: Complexity and Collection Semantics

Do not choose a collection only from theoretical complexity, but understand the common operations.

If you repeatedly search an array by ID:

js
function findById(items, id) {
  return items.find((item) => item.id === id);
}

each lookup scans until a match is found.

An index trades construction/update work and memory for direct lookup:

js
const byId = new Map(
  items.map((item) => [item.id, item])
);

console.log(byId.get("P-100"));

That can be valuable when lookups are frequent and the collection changes in controlled ways.

Set operations

Modern runtimes increasingly provide Set composition methods, but compatibility can vary. A portable intersection can be written as:

js
function intersection(first, second) {
  return new Set(
    [...first].filter((value) => second.has(value))
  );
}

Binary data and DataView

DataView can read different numeric types and byte orders from an ArrayBuffer.

js
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);

view.setUint16(0, 500, false);

console.log(view.getUint16(0, false));

You do not need binary APIs for most CRUD applications, but you should recognize them when working with files, media, hardware protocols, WebSockets, or WebAssembly.

Best Practices

  • Use Objects for records and Maps for dynamic keyed collections.
  • Use Sets for uniqueness/membership.
  • Use weak collections only when object lifetime semantics are part of the problem.
  • Treat JSON as a transport/storage representation, not a clone of the JavaScript object model.
  • Validate parsed JSON before trusting its shape.
  • Use typed arrays only when binary/numeric memory is actually required.

Exercises

Core

Build a Set of unique category names.

Practice

Build a Map keyed by product ID and implement findProduct(id).

Professional Extension

Create serializer/deserializer helpers that convert a Map of inventory quantities to JSON and restore it.

Recap

JavaScript has multiple data structures because different data has different semantics. Choosing the right structure makes intent and performance characteristics clearer.