completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Choosing a data structure

Choose between arrays, objects, Maps, Sets, and WeakMaps by comparing lookup, order, keys, duplicates, grouping, and performance trade-offs.

By the end you can pick a structure with confidence
  • 01
    Tell order apart from findingDecide when a numbered list is the best fit, and when finding a value by its key matters more.
  • 02
    Know the rules for keysExplain how each structure treats repeated values, string keys, object keys, and keys that can go away.
  • 03
    Group a list and think about speedSplit a list into groups with Object.groupBy and Map.groupBy, and think about how work grows without trusting one timing.

Pick the right container for your data

JavaScript gives you several ways to keep a group of values. This lesson compares five of them:

  • An array keeps values in a numbered list.
  • An object keeps values under names, such as theme.
  • A Map keeps values under keys, and a key can be any value.
  • A Set keeps each value only once.
  • A WeakMap sticks extra data onto objects. The data goes away when its object is no longer used.

Each of these is a data structure: a way to arrange data so that some jobs are easy. Your choice also tells other people what your data is. It says "this is a list", or "look this up by name", or "no repeats here".

This lesson builds on earlier lessons: arrays, objects, Map and Set, Set methods, WeakMap and WeakSet, and JSON. You don't need any math. When we talk about speed, we only ask one question: how much more work does the code do when the data gets bigger?

The main rule

Start with the question your code asks most often:

  • "What is the next item?" Use an array.
  • "Do we have this value?" Use a Set.
  • "What value goes with this key?" Use an object, a Map, or a WeakMap.
A cart where order and repeats matterPop out in the code editor (opens in a new tab)JavaScript
const cart = ["tea", "tea"];
console.log(cart.length, cart[0]);

Line 1 makes an array called cart that holds "tea" twice. Line 2 prints the number of items and the first item: 2 tea. The cart keeps both copies, in the order they were added. For this data, order and repeats matter, so an array fits.

Real-life analogyPicking a structure is like packing for a trip

You pack hiking boots in a backpack, not in a filing cabinet. Data works the same way. The right container makes your most common job easy. It also makes the rules clear, such as "no repeats here".

In real life: A suitcase with outfits packed in order
In JavaScript: Array: values in order, repeats allowed
In real life: A phone book sorted by name
In JavaScript: Object or Map: find a value by its key
In real life: A guest list at the door
In JavaScript: Set: each value once; is this one in?
In real life: Sticky notes on gear you borrowed
In JavaScript: WeakMap: extra data stuck onto objects you don't own

The next sections cover four things:

  • Finding a value versus keeping things in order.
  • Keys, and values that appear more than once.
  • How the work grows as the data grows.
  • Splitting a list into groups with Object.groupBy and Map.groupBy.

Finding a value vs keeping order

STEP THROUGH

An array is like a numbered line of people. It is great when position matters: the first task, the last message, or scores sorted from high to low. But say you ask, "Is Ada in the line?" The array may have to check each person, one by one, until it finds Ada.

A Set is like a guest list. It answers one question: is this value in, or not? A Map or an object is like a phone book. You give it a key, and it goes straight to the value for that key. There is no magic here. JavaScript arranges the values inside a Set, a Map, or an object so it can find a key quickly.

A to-do list keeps its repeated taskPop out in the code editor (opens in a new tab)JavaScript
const todos = ["buy tea", "buy tea"];
console.log(todos.includes("buy tea"), todos.length);

Line 1 makes a to-do list with the same task twice. On line 2, todos.includes("buy tea") checks whether the task is in the list. It gives true. todos.length gives 2, because the list still has both entries. So line 2 prints true 2. If you only cared whether the task is there, a Set would say that more clearly.

Real-life analogyA line of people vs a guest list

If you need the third person in line, the line is perfect. If you only need to know whether someone is invited, the guest list is quicker.

In real life: A numbered line of people
In JavaScript: An array, with positions 0, 1, 2, and so on
In real life: Walking down the line, looking for a name
In JavaScript: array.includes(value)
In real life: Checking the guest list at the door
In JavaScript: set.has(value)
Left: line.includes("Ada") checks the cards "Lin", "Kim", and "Ada" one by one, so it takes 3 checks. Right: guests.has("Ada") goes straight to the "Ada" card in the guests Set, in 1 check.line.includes("Ada")"Lin""Kim""Ada"1233 checks, one by oneguests.has("Ada")"Lin""Kim""Ada"guests1 check, straight to it
Both ask, "Is Ada here?" The array checks each value in turn, so a longer list means more checks. The Set goes straight to the value.
Look for a name in an array and in a Set
Step 0 of 6Ready
Your turn: follow the blue line

Step through an array that keeps repeats and a Set that keeps each value once, as both answer the same question.

Running in
  1. script
Next: line 1
const blockedSet = new Set(blockedNames);const name = "bot";const arrayAnswer = blockedNames.includes(name);const setAnswer = blockedSet.has(name);console.log(arrayAnswer, setAnswer);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Both checks give the same answer. So the choice is not about the answer. It is about the rule you mean, and how often your program repeats the check.

Keys and repeated values

STEP THROUGH

A duplicate is a value that appears more than once. Duplicates are not always a mistake. A shopping cart can hold two of the same mug. A search history can hold the same search twice. Arrays allow duplicates, because each entry has its own position. Sets do not: a value is either in the Set or not.

Left: the array ["tea", "tea"] keeps both "tea" cards, at positions 0 and 1, so its length is 2. Right: new Set(["tea", "tea"]) keeps only one "tea" card, so its size is 1.["tea", "tea"]"tea""tea"01length: 2new Set(["tea", "tea"])"tea"Setsize: 1
The same two values go in. The array keeps both, each at its own position. The Set keeps "tea" only once.

Keys have rules too. A plain object's keys are always strings (text) or Symbols. That makes objects a great fit for JSON-style records such as { theme: "dark" }. But it also means the number 1 and the string "1" become the same key. A Map keeps each key as it is. A number stays a number, and an object key stays that exact object.

An object turns both keys into "1"Pop out in the code editor (opens in a new tab)JavaScript
const prices = {};
prices[1] = "number key";
prices["1"] = "text key";
console.log(prices[1]);

Line 1 makes an empty object. Line 2 stores a value under the number key 1. Line 3 stores a value under the string key "1". Both become the same property name, "1", so line 3 replaces the value from line 2. Line 4 prints text key. If the difference between 1 and "1" matters, use a Map.

Watch object keys clash while Map keys stay separate
Step 0 of 9Ready
Your turn: follow the blue line

Watch a plain object turn its keys into strings, while a Map keeps each key as it is.

Running in
  1. script
Next: line 1
const plain = {};plain[1] = "number one";plain["1"] = "string one";const typed = new Map();typed.set(1, "number one");typed.set("1", "string one");typed.set(objectKey, "object key");console.log(plain[1], typed.size, typed.get(objectKey));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The table below compares all five structures. Each row is one structure. Each column answers one question about it.

Array, object, Map, Set, and WeakMap compared
StructureWhat is it best for?Does it keep order?Can a value repeat?What can be a key?How does it find a value?Can you loop over it?Can JSON save it?
ArrayLists in order, such as tasks, queues, and sorted results.Yes, by position: 0, 1, 2, and so on.Yes.Position numbers only.includes checks values one by one.Yes, easily.Yes.
ObjectRecords with fixed names, and simple lookups by a string key.Mostly, but order is not what objects are for.Each key holds one value.Strings and Symbols. Other keys turn into strings.Goes straight to the key.Yes, with Object.keys or Object.entries.Yes.
MapLookups where keys are added and removed often, or keys that aren't strings.Yes, in the order keys were added.Each key holds one value.Any value, even an object.Goes straight to the key.Yes, easily.Not directly. Turn it into an array first.
SetValues that must not repeat, and checking whether a value is in.Yes, in the order values were added.No. Each value is kept once.The values themselves.has goes straight to the value.Yes, easily.Not directly. Turn it into an array first.
WeakMapExtra data stuck onto objects, such as a cache for page elements.No.Each key holds one value.Only objects (and some Symbols).Goes straight to the exact object.No.No.
JSON can't save every structure

JSON is a text format for saving and sending data. It can hold arrays, objects, strings, numbers, true, false, and null. It can't hold a Map, a Set, or a WeakMap. Turn a Map or a Set into an array or a plain object first. Then save or send it.

How the work grows

INTERACTIVE

To think about speed, start with growth: how much more work happens when the data gets bigger? Say a loop checks blocked.includes(user) for every user. Each check may walk through the whole blocked array again. Instead, make a Set from the array once. Then each check can be blockedSet.has(user), which goes straight to the answer.

You may see this written as O(n) and O(1). This way of writing is called Big-O notation:

  • O(n) means the work grows with the number of items, n. Searching an array is usually like this.
  • O(1) means the work stays about the same, however many items there are. A Set check is built to be like this.

For now, think of it as "walking the line" versus "checking the guest list".

Make the Set once, then reuse itPop out in the code editor (opens in a new tab)JavaScript
const blocked = ["bot", "spam"];
const blockedSet = new Set(blocked);
console.log(blocked.includes("bot"), blockedSet.has("bot"));

Line 1 is the list you start with. Line 2 makes a Set from it. That takes some work, but only once. Line 3 prints true true, because both answer the same question. For one small check, the array alone is simpler. For many checks, reusing the Set saves walking the array again and again.

Time includes and has on your device
The code being timedPop out in the code editor (opens in a new tab)JavaScript
const items = Array.from({ length: 1000 }, (_, i) => "item-" + i);const set = new Set(items);items.includes("item-999"); // may scan many itemsset.has("item-999");       // asks the Set lookup table
Timed here

No numbers yet. Press Time it to run the code in your browser.

How the work grows matters more than the exact number. An array check usually looks at the values one by one. A Set check goes straight to the answer.

Try it yourself

Pick a size, then time it on this device.

Timings change with the browser, the device, the battery, and what else is running. Treat them as what happened here, not as a promise.

A benchmark is a test that times your code. Be careful with its numbers. Timings change with your computer, your browser, battery saving, and whatever else is running. The playground only runs when you click. It does five short runs and shows the median, which is the middle result. The numbers are true for this device only. That's why the lesson's tests check that the code is correct, not how fast it is.

Group a list with Object.groupBy and Map.groupBy

STEP THROUGH

Grouping splits one list into smaller lists, called groups. Each item goes into a group based on a key. JavaScript added two tools for this in 2024 (ES2024):

  • Object.groupBy(items, callback) gives back a plain object. Use it when each group's key is a string (or a Symbol), such as "paid".
  • Map.groupBy(items, callback) gives back a Map. Use it when the keys are objects, or other values that are not strings.

The callback is a function you pass in. JavaScript calls it once for each item. It returns the key of the group that item belongs in.

Group orders by their statusPop out in the code editor (opens in a new tab)JavaScript
const orders = [
  { id: 1, status: "paid" },
  { id: 2, status: "new" },
  { id: 3, status: "paid" },
];
const byStatus = Object.groupBy(orders, order => order.status);
console.log(byStatus.paid.length);

The callback on line 6, order => order.status, returns each order's status. JavaScript makes one array for each status it sees: paid and new. Line 7 reads the paid group and prints 2. Inside each group, the orders keep the order they had in the first list.

Three orders, 1 paid, 2 new, and 3 paid, are grouped by order.status. The paid group holds orders 1 and 3. The new group holds order 2.1 paid2 new3 paidorder.status1 paid3 paidpaid2 newnewbyStatus.paidbyStatus.new
The callback gives each order's status. Orders with the same status land in the same group, still in their first order: paid holds 1 and 3, and new holds 2.

Does your browser have these tools? Checking your browser once the page has loaded…

Group products by their category
Step 0 of 5Ready
Your turn: follow the blue line

Group an array with Object.groupBy, then look at the plain result it gives back.

Running in
  1. script
Next: line 1
  { name: "Notebook", category: "stationery" },  { name: "Pen", category: "stationery" },  { name: "Tea", category: "pantry" },];const byCategory = Object.groupBy(products, item => item.category);const prototype = Object.getPrototypeOf(byCategory);console.log(byCategory.stationery.map(item => item.name).join(", "));console.log(prototype === null);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

When the group key is an object, use Map.groupBy. Here, each product's owner is an object.

Map.groupBy when the key is an objectPop out in the code editor (opens in a new tab)JavaScript
const kitchen = { team: "kitchen" };const office = { team: "office" };const products = [  { name: "Tea", owner: kitchen },  { name: "Notebook", owner: office },];const byOwner = Map.groupBy(products, item => item.owner);console.log(byOwner.get(kitchen).map(item => item.name));

Line 7 groups the products by their owner object. Line 8 asks for the group of the exact kitchen object. It prints an array with one name in it, "Tea".

One more detail about Object.groupBy. Most objects borrow methods, such as hasOwnProperty, from a shared object called Object.prototype. That shared object is the object's prototype. The result of Object.groupBy has no prototype, so it borrows nothing. This keeps it clean: a group named toString can't clash with a built-in method. But it also means result.hasOwnProperty(key) doesn't work. Use Object.hasOwn(result, key) instead.

Choose with a few questions

INTERACTIVE

You don't have to choose from memory. Ask yourself these questions:

  • Does the order of the values matter?
  • Do I find values by a key?
  • Can the same value appear twice?
  • Are the keys objects?
  • Should the extra data go away when its object is no longer used?
  • Do I need to save the data as JSON?

The helper's answer is a first guess, not a rule. You can also use two structures together. For example, a Set can remove duplicates, and then an array can show the values in order. Pick the structure that makes the next job easy. Switch to another structure when the job changes, such as just before you save.

Answer a few questions, get a structure
Your answers, passed to the helperPop out in the code editor (opens in a new tab)JavaScript
recommendStructure({  ordered: true,  keyed: false,  duplicates: true,  objectKeys: false,  weakLifetime: false,  json: false,});
Suggested structure
StructureArray
WhyAn array is the usual choice for a list in order that can hold repeats.
Code to start withPop out in the code editor (opens in a new tab)JavaScript
const tasks = ["plan", "build", "ship"];
Try it yourself

Try: Array. An array is the usual choice for a list in order that can hold repeats.

The helper follows fixed rules from this lesson. Its answer is a first guess that you can change.
Which structure fits?
  • Keep each visitor ID once while the page is open
  • Show a to-do list in the order the user arranged it
  • Read settings such as theme and language by name, and save them as JSON
  • Store a measurement for each page element, without keeping removed elements in memory
  • Group orders by status names such as paid and shipped
  • Handle tasks in the order they arrived: first in, first out
  • Count how often each word appears
  • Keep each tag once, in the order it first appeared
Try it yourself
0 of 8 correct

Sort each real situation under the structure you would try first. Real apps sometimes use two structures together, but each card here has one best first choice.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Where you'll use this

These five choices show up in everyday app code:

Five common jobs, five structuresPop out in the code editor (opens in a new tab)JavaScript
// 1. Ordered UI stateconst tasks = ["write", "review", "ship"]; // 2. JSON-friendly settingsconst settings = { theme: "dark", density: "compact" }; // 3. Counting with a Mapconst counts = new Map();counts.set("bug", (counts.get("bug") ?? 0) + 1); // 4. Fast membership with a Setconst selectedIds = new Set([101, 104, 108]); // 5. Metadata for objects you do not ownconst measurements = new WeakMap();
  • Line 2: an array keeps tasks in the order the page shows them.
  • Line 5: an object holds settings that you save as JSON.
  • Line 9: a Map counts things. counts.get("bug") ?? 0 gives 0 the first time, because the key isn't there yet.
  • Line 12: a Set holds the IDs a user has selected, so you can check them quickly.
  • Line 15: a WeakMap sticks measurements onto objects that other code made.

It is normal to switch between structures. For example, you get JSON as objects and arrays. You build a Map from it, so repeated lookups are quick. Then you turn it back into an array or an object before you save it. The structure you save doesn't have to be the structure you work with.

Common misconceptions

"A Map is always better than an object."

Objects are still the best fit for records with fixed names and for JSON. A Map is better when you add and remove keys often, or when the keys are objects.

"A Set sorts or cleans the data."

A Set only removes duplicates. It keeps values in the order they were first added. It does not sort them.

"A WeakMap is just a faster Map."

A WeakMap is about how long data lives, not speed. Its keys must be objects. You can't loop over it, because an entry can vanish once nothing else uses its key object.

"Objects keep every key exactly as written."

A plain object turns every key into a string, unless the key is a Symbol. Use a Map when number keys, object keys, or the type of the key matter.

"A benchmark tells the whole truth."

A small timing test is a hint, not a rule. First choose the structure that fits the job. Measure speed only where real code is slow.

"Switching structures loses the meaning of the data."

Switching is useful when the next step needs a different shape. A JSON response can become a Map for quick lookups, then become an array again before you save it.

Practice: choose and change structures

5 EXERCISES
Exercise 1 · Warm-upPick a structure for a cart

A shopping cart must show items in the order they were added. The same item can be added twice. Which structure fits best?

Starter codePop out in the code editor (opens in a new tab)JavaScript
// Best structure for a shopping cart shown in item order, duplicates allowed:
console.log("Array");

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeSwap includes for a Set

    Read the code. It keeps every user who is not blocked, and keeps repeated users too. What does it print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const users = ["Ada", "Lin", "Grace", "Ada"];
    const blocked = ["Lin"];
    const blockedSet = new Set(blocked);
    console.log(users.filter(user => !blockedSet.has(user)).join(","));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeGroup orders by status

      The code uses Object.groupBy to group orders by their status. Which IDs does it print for the paid group?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const orders = [
        { id: 1, status: "paid" },
        { id: 2, status: "new" },
        { id: 3, status: "paid" },
      ];
      const byStatus = Object.groupBy(orders, order => order.status);
      console.log(byStatus.paid.map(order => order.id).join(","));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeCount with a Map

        Follow the loop one word at a time. What count is stored for the word map?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const words = ["map", "set", "map"];
        const counts = new Map();
        for (const word of words) {
          counts.set(word, (counts.get(word) ?? 0) + 1);
        }
        console.log(counts.get("map"));

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeRemove repeats but keep the order

          The code turns an array into a Set and back into an array. In what order do the tags print?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const tags = ["js", "css", "js", "html", "css"];
          console.log([...new Set(tags)].join(","));

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS
            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7You need to show messages in the exact order they arrived, including repeats. What should you start with?

              Choose an answer to see the explanation.

            2. Question 2 of 7What size does this Set have?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const ids = [1, 1, 2];
              console.log(new Set(ids).size);

              Choose an answer to see the explanation.

            3. Question 3 of 7An object gets the keys 1 and "1". What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const data = {};
              data[1] = "number";
              data["1"] = "string";
              console.log(data[1]);

              Choose an answer to see the explanation.

            4. Question 4 of 7Which structure lets you use an object as a key, and also loop over all its entries?

              Choose an answer to see the explanation.

            5. Question 5 of 7How many items are in the book group?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const products = [
                { type: "book", name: "JS" },
                { type: "tool", name: "Pen" },
                { type: "book", name: "CSS" },
              ];
              const grouped = Object.groupBy(products, item => item.type);
              console.log(grouped.book.length);

              Choose an answer to see the explanation.

            6. Question 6 of 7Which timing playground gives you honest numbers?

              Choose an answer to see the explanation.

            7. Question 7 of 7A Map gets the keys 1 and "1". What size does it print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const map = new Map();
              map.set(1, "number");
              map.set("1", "string");
              console.log(map.size);

              Choose an answer to see the explanation.

            Key takeaways

            • An array is a list in order. Use it when position, sorting, or repeated values matter.
            • An object is a record with string keys. It is easy to save as JSON.
            • A Map keeps every key as it is, even objects. A Set keeps each value once and checks for a value quickly.
            • A WeakMap sticks data onto objects. It doesn't keep those objects alive, and you can't loop over it.
            • Use Object.groupBy when group keys are strings, and Map.groupBy when they are objects or other values.

            Remember the one-liner.
            Choose the structure that best answers your most common question: what comes next, what goes with this key, is this value in, or how long should this data live.

            Up next: Date & time.

            CompleteFrontend Clear concepts. Working examples.