1. Courses
  2. Developer Foundations
  3. JavaScript Deep Dive
  4. Stop 4

40 minIntermediate

Working with data: arrays, Maps, JSON, dates and money

Most business code takes rows of data, reshapes them and adds them up. This lesson covers the tools for that and the four places where they quietly give wrong answers: sorting, JSON, time zones and decimal money.

By the end you can

  • Chain filter, map, Object.groupBy, reduce and toSorted into a per-store summary
  • Sort numbers correctly, and avoid sorting shared arrays in place
  • Say what JSON loses (dates, Maps, BigInt) and when to use a Map or a Set
  • Compute a store's business day in its own time zone on a server that runs in UTC
  • Explain why 0.1 + 0.2 is not 0.3, and calculate tax in integer cents before formatting it as money

The everyday picture

At month end, the accounts team gets a box of paper sale slips from every store. Nobody adds them up in one go. One person removes the voided slips. The next writes each amount in cents on the corner, so nobody has to deal with "$4.55". A third sorts the slips into one tray per store. A fourth adds up each tray, and the last one puts the trays in order, biggest first, for the manager's report.

Each person does one simple job and passes the result on. That is how good data code looks in JavaScript: a pipeline of small steps. And the mistakes in this lesson are the mistakes that team could make: sorting slips by the first digit instead of the amount, writing the wrong date on a slip from 12:30 am, or rounding each slip's tax differently from the till.

Array methods: one job per step

Each array method takes a function and applies it to every item. Learn what shape each one returns, and the pipeline reads itself:

  • map(fn): a new array with one output per input.
  • filter(fn): a new array with the items where fn returned truthy.
  • find(fn): the first matching item, or undefined. findIndex gives its position, or -1.
  • some(fn) / every(fn): a boolean. Is any item a match? Are all items a match?
  • reduce(fn, start): one value built from all the items, such as a total.
  • flatMap(fn): like map, but each item may produce zero, one or several outputs, which are joined into one flat array.
JavaScript
// methods.js: the everyday array methods on one GRN (goods received note)
const grn = [
  { articleCode: '100234', ordered: 40, received: 40, damaged: 0 },
  { articleCode: '200871', ordered: 10, received: 8, damaged: 1 },
  { articleCode: '310045', ordered: 24, received: 24, damaged: 2 },
];

console.log(grn.map((l) => l.received - l.damaged));        // one output per input
console.log(grn.filter((l) => l.received < l.ordered));     // keep the matches
console.log(grn.find((l) => l.damaged > 0)?.articleCode);   // first match, or undefined
console.log(grn.some((l) => l.damaged > 0), grn.every((l) => l.received > 0));
console.log(grn.reduce((sum, l) => sum + l.damaged, 0));    // many values to one

// flatMap: each line can produce zero, one or many outputs
const issues = grn.flatMap((l) => [
  ...(l.received < l.ordered ? [`${l.articleCode}: short by ${l.ordered - l.received}`] : []),
  ...(l.damaged > 0 ? [`${l.articleCode}: ${l.damaged} damaged`] : []),
]);
console.log(issues);
console.log([].every((l) => l.received > 0));               // true for an empty list!
Output
[ 40, 7, 22 ]
[ { articleCode: '200871', ordered: 10, received: 8, damaged: 1 } ]
200871
true true
3
[ '200871: short by 2', '200871: 1 damaged', '310045: 2 damaged' ]
true

Two traps hide in that output. every on an empty array is true, so "all lines received" is true for a GRN with no lines; check the length too. And always give reduce its start value: [].reduce((sum, l) => sum + l.qty) throws TypeError: Reduce of empty array with no initial value, which is exactly what happens on the first day a new store has no sales.

None of these methods change the original array. That makes them safe to chain and safe on shared data (Objects, arrays and references). sort is different.

Quick check

A store's GRN arrives with zero lines. What does lines.some((l) => l.damaged > 0) return?

Loading the rest of the lesson…