Dataslope logoDataslope

Nested Data Structures

Modelling the real world by combining arrays and objects — and the small skills (safe navigation, deep updates, JSON) that make it pleasant.

A single object or array can only get you so far. Real-world data almost always has structure inside structure: an order has line items, each line item has a product, each product has tags. Once you can navigate and update nested data confidently, you can model just about anything.

Mental model: shapes inside shapes

Take a moment to read this object slowly:

Code Block
JavaScript ES2023+

That single value packs a lot of information. The skill is being able to picture its shape — to know where things live.

Reading deep paths

To read a value from a deeply nested structure, chain dots and brackets:

order.customer.address.city          // "London"
order.items[0].title                 // "Notebook"
order.items[order.items.length - 1]  // last line item

The hazard is missing intermediate values. If order.customer is undefined, then order.customer.address will throw a TypeError: Cannot read properties of undefined (reading 'address'). A classic source of beginner crashes.

Optional chaining: ?.

Modern JavaScript has a special operator for safe navigation. If the value before ?. is null or undefined, the whole expression returns undefined instead of throwing.

Code Block
JavaScript ES2023+

The pair of ?. and ?? (nullish coalescing) is the modern JavaScript way to safely read possibly-missing data and fall back to a default. Together they replace many lines of defensive if (x && x.y && x.y.z) checks.

Walking nested data

The natural way to walk nested data is nested loops (or recursion). Let's add up the total cost of the order above:

Code Block
JavaScript ES2023+

That is just an "accumulator over a list" loop, walking one level of nesting. The same pattern scales to any depth.

A two-level example: students and grades

Code Block
JavaScript ES2023+

Outer loop walks each student. Inner loop walks that student's grades. Two levels of nesting; two for loops. The pattern is mechanical once you see it.

Immutable updates: deep edits without mutation

Earlier we praised the immutable update style:

const updated = { ...original, name: "new name" };

That works perfectly for a one-level object. What about updating something deep inside a nested structure?

Code Block
JavaScript ES2023+

Notice we had to spread each level we wanted to copy. The remaining nested objects are shared (still by reference), which is fine — they're unchanged.

Real codebases sometimes use helper libraries (like Immer) to make deep updates pleasant. For a first pass, the explicit spread style is clearest.

Updating an item inside an array

Updating one element of an array, immutably, is a common task. Use .map to produce a new array where one element is changed:

Code Block
JavaScript ES2023+

The unchanged elements pass through .map unmodified. The matching element is replaced by a new object via spread.

JSON: data as text

A nested structure of objects, arrays, numbers, strings, booleans, and null is precisely the set of things you can store as JSON (JavaScript Object Notation). JSON is the universal exchange format of the web — every API you'll ever call either accepts or returns JSON.

JavaScript ships with two functions for converting between JSON text and live values:

Code Block
JavaScript ES2023+

A neat side-effect: JSON.parse(JSON.stringify(x)) is a quick-and-dirty way to make a deep copy of plain data. It won't handle functions, dates, or circular structures, but for pure data it's a useful trick.

Common shapes you'll meet

It helps to recognise a few recurring shapes:

  • Array of objects (a table-like dataset)

    const users = [
      { id: 1, name: "Ada" },
      { id: 2, name: "Linus" },
    ];
  • Object of arrays (data grouped by category)

    const grouped = {
      admins:  ["Ada", "Grace"],
      members: ["Linus"],
    };
  • Map-like object (key-keyed lookups)

    const usersById = {
      1: { id: 1, name: "Ada" },
      2: { id: 2, name: "Linus" },
    };

Almost every API response, configuration file, or database row you ever encounter is some combination of these.

A multi-file nested-data example

A small program that reads a list of orders, computes totals per customer, and prints a tidy summary. The shape of the data is non-trivial; the code is straightforward because we keep each step small.

Code Block
JavaScript ES2023+

Notice the inner nested loops (for order... for item...) — they mirror the shape of the data. When the data has two levels of nesting, the loop usually does too.

Challenge

Challenge
JavaScript ES2023+
Safely read a nested property

Write a function getPath(obj, path, defaultValue) that:

  • Takes an object obj, a path string like "customer.address.city", and a default value.
  • Returns the value at that nested path, or the default if any step is missing.
  • Must not throw, even if intermediate properties are missing or null.

Examples:

  • getPath({ a: { b: { c: 42 } } }, "a.b.c", 0)42
  • getPath({ a: { b: {} } }, "a.b.c", 0)0
  • getPath({ a: null }, "a.b.c", "n/a")"n/a"
  • getPath({}, "x", "n/a")"n/a"

QuestionSelect one

Why is optional chaining (a?.b?.c) so useful when reading deeply nested data?

It runs faster than ordinary property access

It automatically creates missing properties

It safely returns undefined if any link in the chain is null or undefined, instead of throwing a TypeError and crashing the program

It is a faster form of array indexing

On this page