Choosing the Right Data Structure

Intermediate · 18 min read · ▶ live playground · ✦ checkpoint

The same data can look "right" in four different containers until you ask what the program needs to do with it. Choosing the right JavaScript data structure means naming the job first: ordered list, described thing, keyed lookup, or unique membership.

Start with the question

Before choosing a structure, ask what question your next line of code will ask most often:

| If the code mostly asks... | Reach for... | Why | | --- | --- | --- | | "What is first, next, last, sorted, filtered?" | Array | Arrays are ordered lists built for walking, transforming, and reordering values. | | "What are this thing's named fields?" | Object | Objects describe one thing with known property names. | | "What value belongs to this key?" | Map | Maps are ordered key-value collections with any key type and a built-in size. | | "Have I seen this value already?" | Set | Sets store unique values and answer membership questions directly. |

That "pressure" test is the habit. If your code keeps calling find to locate one item by ID, the data is asking to become a Map. If your code keeps checking duplicates, it is asking for a Set. If your code keeps carrying fields like name, role, and active, that one record wants to be an object.

const user = { id: "u1", name: "Mina", role: "admin" };
const users = [
  user,
  { id: "u2", name: "Rae", role: "editor" },
];
const usersById = new Map(users.map(person => [person.id, person]));
const selectedIds = new Set(["u1"]);
 
console.log(user.name);
console.log(users.map(person => person.name).join(", "));
console.log(usersById.get("u2").role);
console.log(selectedIds.has("u1"));

Mina, then Mina, Rae, then editor, then true.

Those four structures are not rivals. They are four tools around the same data, each answering a different question cleanly.

Lookups: object or Map

A lookup is data arranged so one value can quickly lead you to another. If your keys are stable strings and you are describing a plain record, an object is often enough:

const lessonsBySlug = {
  maps: "Maps",
  sets: "Sets",
  choosing: "Choosing Structures",
};
 
console.log(lessonsBySlug.sets);
console.log(lessonsBySlug["choosing"]);
 
const lessonProgress = new Map([
  [{ slug: "maps" }, "reviewed"],
  [{ slug: "sets" }, "ready"],
]);
 
const mapsLesson = Array.from(lessonProgress.keys())[0];
 
console.log(lessonProgress.get(mapsLesson));
console.log(lessonProgress.size);

Sets, then Choosing Structures, then reviewed, then 2.

Use the object when the keys are property names and the result is JSON-shaped data. Use the Map when the collection behavior matters: any key type, entries in insertion order, a real size, and method names that say "this is a lookup table."

Queues are arrays with a rule

A queue is an ordered list where new items join the back and work leaves from the front. Arrays fit because order is the whole point:

const reviewQueue = ["Mina", "Rae"];
 
reviewQueue.push("Sol");
 
const firstReviewer = reviewQueue.shift();
const secondReviewer = reviewQueue.shift();
 
console.log(firstReviewer);
console.log(secondReviewer);
console.log(reviewQueue.join(", "));
console.log(reviewQueue.length);

Mina, then Rae, then Sol, then 1.

This is not a new built-in type. It is an array used with a discipline: push adds to the back, shift removes from the front. If you start needing named fields on each queue item, make the items objects inside the array.

Registries are Maps

A registry is a collection of known things your program can find by key. Registries usually want Map behavior because the registry is a living collection, not one JSON-shaped record.

const commandRegistry = new Map();
 
commandRegistry.set("save", {
  label: "Save draft",
  run() {
    return "saved";
  },
});
 
commandRegistry.set("publish", {
  label: "Publish lesson",
  run() {
    return "published";
  },
});
 
const command = commandRegistry.get("publish");
 
console.log(command.label);
console.log(command.run());
console.log(commandRegistry.has("archive"));
console.log(commandRegistry.size);

Publish lesson, then published, then false, then 2.

An object can also hold function-valued properties, but Map makes registration and lookup feel like the main event. That becomes clearer as entries are added, removed, or iterated.

Tags are Sets

Tags are labels attached to something so you can group, filter, or search it. A tag either exists or it does not, so a Set is a natural fit:

const articleTags = new Set(["javascript", "data", "javascript"]);
const requiredTags = new Set(["javascript", "beginner"]);
 
articleTags.add("structures");
 
const sharedTags = articleTags.intersection(requiredTags);
const missingTags = requiredTags.difference(articleTags);
 
console.log(articleTags.size);
console.log(articleTags.has("data"));
console.log(Array.from(sharedTags).join(", "));
console.log(Array.from(missingTags).join(", "));

3, then true, then javascript, then beginner.

If you care about tag order for display, turn the Set into an array at the edge with [...articleTags] or Array.from(articleTags). Keep the Set as the source of truth while the code is asking membership questions.

Try it — model the same workflow four ways

Run this, then inspect tasks, settings, taskRegistry, and activeTags at the prompt. Each one exists because the program asks a different kind of question.

javascript — live consolereal engine
⌘/Ctrl + Enter to run

This is what real modeling often looks like: not one perfect container, but a few small containers that each make one operation obvious. The array carries order, the object carries settings, the Map carries direct lookup by ID, and the Set carries membership.

Make the structure match the change

Choose structures by what changes over time:

  • Use an Array when items are added, removed, sorted, filtered, or processed in order.
  • Use an Object when one thing has named fields you read and update together.
  • Use a Map when entries are added and read by key as a collection.
  • Use a Set when values are added for uniqueness or membership.

The wrong structure does not always break immediately. It makes later code noisy. A queue stored as an object needs fake numeric keys. Tags stored as an array invite duplicate cleanup. A registry stored as a plain object may work until keys stop being plain property names.

Checkpoint

Answer all three to mark this lesson complete

Good structure choices make the happy path short and readable. Next, Section 9 starts the work of making programs robust when inputs are missing, assumptions fail, and errors need to be handled deliberately.

+50 XP on completion