The Data Cruncher

Intermediate · 90 min build · 🏆 milestone project

You have enough JavaScript now to build something that feels like a real app core. This project is a data cruncher, a program that takes structured data in, validates it, transforms it, summarizes it, and saves it back out as text.

Your goal is a budget tracker that models spending as arrays of objects, crunches the data with functions and array methods, survives bad rows gracefully, and round-trips through JSON. Keep it console-first: no page UI, no network, no browser storage. The data is the app.

What you'll build

You will build a budget workspace with five jobs:

  • read budget data from JSON text
  • validate and normalize each expense
  • summarize paid and unpaid spending with map, filter, and reduce
  • format money from whole-number cents with Intl.NumberFormat
  • save a clean budget back to JSON text and parse it again

A scaffold is starter code with named places for your work. The acceptance criteria are checks you can run yourself to prove each stage works. Persistence means saving data in a form that can cross a boundary; here, that boundary is JSON text, not a real file or browser storage yet.

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

Checkpoint 1: model the budget data

Start by reading the starter data. Each expense is one object, and the budget holds an array of those expense objects. The source of truth is the one value your program trusts most; for this project, make the normalized budget your source of truth after validation.

Acceptance criteria:

  • Each expense has an id, label, category, cents, and paid.
  • Money is stored as whole-number cents, never as dollars with decimals.
  • Missing optional fields get safe defaults with ??.
  • The owner name is read with ?. and ??.
  • You can add a new valid expense object and see it affect the report.

Checkpoint 2: validate at the boundary

Treat the JSON input as outside data. The boundary is the front desk from 9.2: check the data there, then let the inside of the program work with clean values.

Acceptance criteria:

  • Invalid JSON is caught by the top-level try / catch and prints a clear message.
  • A non-object budget throws TypeError: Budget must be an object.
  • A missing expenses array throws TypeError: Budget needs an expenses array.
  • A bad expense row is rejected without stopping the whole report.
  • Rejected rows include enough detail to find the bad row and the reason.

Checkpoint 3: crunch the numbers

Now make the data earn its keep. Use arrays for ordered expenses, objects for records, a Map for category totals, and array methods for transformation.

Acceptance criteria:

  • filter separates paid expenses from unpaid expenses.
  • reduce calculates paid and unpaid totals in cents.
  • reduce also builds category totals for paid expenses.
  • map creates display lines for the report.
  • Displayed money uses Intl.NumberFormat, while stored values stay as cents.

Checkpoint 4: save and reload JSON

Your project does not need real storage yet. It needs the data boundary: turn clean JavaScript values into JSON text, then parse that text back into JavaScript values.

Acceptance criteria:

  • JSON.stringify(value, null, 2) creates readable JSON text.
  • JSON.parse(savedText) loads the saved text back into a value.
  • The saved JSON contains clean accepted expenses, not rejected rows.
  • Parsing creates fresh objects, so you do not depend on object identity after reload.
  • Changing the normalized budget changes the saved JSON output.

Checkpoint 5: debug the report

Use the 9.3 habit: do not guess. When a total looks wrong, write down what you expected, pause or log the narrowest value that could prove it, then conclude what you ruled out.

Acceptance criteria:

  • You can explain one bug using observation → hypothesis → experiment → conclusion.
  • console.table(report.expenses) helps inspect the cleaned rows in DevTools.
  • A wrong total is traced to either filtering, reducing, or bad input.
  • You remove temporary debugger statements or noisy logs after the investigation.
  • The final starter run has no uncaught errors.

Stretch goals

Choose one or two after the base tracker works:

  • Add a Set of allowed categories and reject expenses outside it.
  • Add a Map of category budgets and report which categories are over budget.
  • Add a paid: false report that lists upcoming unpaid expenses.
  • Sort accepted expenses from largest to smallest without mutating the original array.
  • Add a compactSave function that writes one-line JSON and a prettySave function that writes readable JSON.

How to get unstuck

If function boundaries get blurry, revisit declaring and calling functions and parameters in depth. If the array pipeline gets tangled, compare it to map, filter, reduce. If object shapes feel slippery, go back to object literals and properties, destructuring, spread, and rest, and references: copy vs alias. If saving text feels odd, review JSON: the language of data. If validation or error handling gets noisy, use Errors & Exceptions, Defensive Coding & Validation, and Debugging in DevTools.

You have finished Part II when this project feels like a small data engine, not a pile of examples. Part III opens the hood on how JavaScript runs work over time.

+50 XP on completion