Chapter 3 · Data
Objects
Step 1 of 22 · Concept
An object holds values by name
By the end of this lesson you keep records, loop over them, and save them as text.
const p = { name: "Ada", age: 36 };
const key = "age";
- An object holds values by name. Each property is a key and its value.
p.nameis"Ada".p[key]reads the key in the boxkey:36.- A key with a space needs quotes:
{ "first name": "Ada" }, read asx["first name"]. - On the heap, as an array: the box holds an arrow.
Step 2 of 22 · Concept
How console.log shows an object
| you log | console.log shows |
|---|---|
{ name: "Ada", age: 36 } | { name: 'Ada', age: 36 } |
{ "first name": "Ada" } | { 'first name': 'Ada' } |
{} | {} |
| longer than about 70 characters | {, then one property per line, then } |
Keys show bare, unless they need quotes, such as a key with a space. Text values show in single quotes.
Step 3 of 22 · Quiz
How does it print?
What does this program print?
const place = { city: "Oslo", "zip code": 150 };
console.log(place);
Step 4 of 22 · Concept
Add, change, delete
| write | it does |
|---|---|
p.age = 37 | changes the property age |
p.city = "London" | adds a property: p had no city |
delete p.age | removes the property age |
p.email | undefined: a missing property, no error |
The box p is a const, and its object still changes, as a const array does (lesson 10).
Step 5 of 22 · Concept
When the object is missing too
p.email is undefined. Reading a property of undefined stops the program (lesson 5).
with p.address missing | gives |
|---|---|
p.address.city | TypeError: Cannot read properties of undefined (reading 'city') |
p.address?.city | undefined: ?. stops at undefined |
p.address?.city ?? "none" | "none" |
a ?? b gives b when a is undefined or null, else a. So 0 ?? 5 is 0, while 0 || 5 picks 5:
|| skips a value that counts as false, such as 0.
Step 6 of 22 · Quiz
What is left?
p has no city. Type the missing line.
const p = { name: "Ada" };
p.age = 36;
delete p.name;
console.log(p.name);
console.log(p.city ?? "none");
console.log(p);
Step 7 of 22 · Demo
See it run
Run it and compare with your answer. Then add console.log(p.address?.city); and run it: undefined. Take out the
? and run it again: now it is the TypeError.
Step 8 of 22 · Concept
Every key and value
on prices, { tea: 2.5, cake: 3 } | gives |
|---|---|
Object.keys(prices) | [ 'tea', 'cake' ] |
Object.values(prices) | [ 2.5, 3 ] |
Object.entries(prices) | [ [ 'tea', 2.5 ], [ 'cake', 3 ] ] |
Each entry is a pair, [key, value]. A for...of over the entries takes each pair apart, by position:
for (const [key, value] of Object.entries(prices)) {
console.log(key, value);
}
Step 9 of 22 · Demo
A loop over a price list
Run it. Then add prices.juice = 1.75; after line 1 and run it again: the loop finds the new key.
Step 10 of 22 · Concept
Number keys go first
Keys keep the order they were added, with one exception. Keys that are whole numbers, such as "10", come first,
in number order.
const s = {};
s.Zoe = 3;
s["10"] = 1;
s.Ann = 2;
s["2"] = 5;
console.log(s) shows { '2': 5, '10': 1, Zoe: 3, Ann: 2 }.
Step 11 of 22 · Quiz
Which key comes first?
Zoe is added first. What does this program print?
const s = {};
s.Zoe = 3;
s["10"] = 1;
s.Ann = 2;
console.log(Object.keys(s).join(" "));
Step 12 of 22 · Demo
See it run
Run it and compare with your answer. Then add s["2"] = 5; before the last line and run it again.
Step 13 of 22 · Exercise
Your turn: a price list
Log one line per item with its price, then the total. The program must print:
tea: 2.50
cake: 3.00
juice: 1.75
Total: 7.25
The loop of the step A loop over a price list logs each item. Add each value to total in the same loop.
A for (const [key, value] of Object.entries(prices)) loop. Inside it, a console.log with a template literal, then total += value;.
console.log(`${key}: ${value.toFixed(2)}`);
Step 14 of 22 · Concept
Arrays of objects
A record is an object for one thing: a person, an order. An array of them keeps many.
const people = [
{ name: "Ada", score: 90 },
{ name: "Alan", score: 85 },
];
| write | means |
|---|---|
people[1].name | "Alan": item 1, then its name |
{ name, score } | { name: name, score: score }: a box's name as the key |
const { name, score } = p; | two boxes, filled from p's properties by name |
Step 15 of 22 · Concept
{ ...a } copies one level only
const a = { score: 1, tags: ["x"] };
const b = a;
const c = { ...a };
b.score = 2;
c.tags.push("y");
{ ...a } makes a new object with the same keys. It copies one level only: its tags gets a copy of the arrow, so
both objects share one array.
Step 16 of 22 · Quiz
What did the copy share?
c is a copy made by { ...a }. Type the missing line.
const a = { score: 1, tags: ["x"] };
const b = a;
const c = { ...a };
b.score = 2;
c.tags.push("y");
console.log(a.score, c.score);
console.log(a.tags.join(","));
Step 17 of 22 · Demo
See it run
Run it and compare with your answer. b changed a's score; c kept its own.
Step 18 of 22 · Concept
Objects as text: JSON
JSON writes an object as text, to save it in a file or send it.
| write | gives |
|---|---|
JSON.stringify(p) | {"name":"Ada","age":36}: one line |
JSON.stringify(p, null, 2) | over several lines, indented by 2 spaces (null: no other option) |
JSON.parse(text) | a new object, made from the text |
Step 19 of 22 · Demo
Text to records and back
Run it. A template literal may run over lines. JSON.parse makes two records from it; record builds a third
with the shorthand { name, score }.
Then add , null, 2 after people[2] and run it again.
Step 20 of 22 · Check yourself
Check yourself: a gradebook
The input is a JSON array of three records, each with a name and a score:
[{"name":"Alan","score":85},{"name":"Ada","score":90},
{"name":"Grace","score":88}]
- Parse the input into records.
- Log each record as
Alan: 85. - Log the name with the best score:
Best: Ada.
The output must be:
Alan: 85
Ada: 90
Grace: 88
Best: Ada
Keep the best record so far in a let, starting with the first one. One loop logs each record and, when its score is higher, makes it the best.
Step 21 of 22 · Challenge · optional
Challenge: a real copy
Optional.
Change line 2 so that copy gets tags of its own: the push must leave ada's tags alone. The output must be:
math
math, code
{ ...ada } copies one level. The copy's tags points at ada's array, so the push shows in both.
structuredClone(x) copies every level: the object, the arrays in it, and what they hold.
const copy = structuredClone(ada);
Step 22 of 22 · Recap
Recap
- An object holds values by name:
p.name,p["first name"],p[key]; a missing one isundefined. ?.stops atundefined, and??gives a default forundefinedornull.for (const [key, value] of Object.entries(p))walks every property; whole-number keys come first.const b = ashares one object;{ ...a }copies one level only.JSON.stringifyturns an object into text,JSON.parsemakes it back.
Next: lesson 12 hands functions to arrays: map, filter and sort.
Review
4 questions from this lesson, in a new order each time. Answer from memory: 3 of 4 right on the first try passes. On a day after you finish the lesson, a pass earns 15 XP.
The review needs JavaScript.
const p = { name: "Ada" };
p.age = 36;
delete p.name;
console.log(p.name);
console.log(p.city ?? "none");
console.log(p);
const prices = { tea: 2.5, cake: 3 };
console.log(Object.keys(prices).join(", "));
for (const [key, value] of Object.entries(prices)) {
console.log(`${key}: ${value.toFixed(2)}`);
}
const s = {};
s.Zoe = 3;
s["10"] = 1;
s.Ann = 2;
console.log(Object.keys(s).join(" "));
const prices = { tea: 2.5, cake: 3, juice: 1.75 };
let total = 0;
// log each item and its price; add it to total
console.log(`Total: ${total.toFixed(2)}`);
const a = { score: 1, tags: ["x"] };
const b = a;
const c = { ...a };
b.score = 2;
c.tags.push("y");
console.log(a.score, c.score);
console.log(a.tags.join(","));
const text = `[{"name":"Ada","score":90},
{"name":"Alan","score":85}]`;
const people = JSON.parse(text);
for (const p of people) {
const { name, score } = p;
console.log(`${name}: ${score}`);
}
function record(name, score) {
return { name, score };
}
people.push(record("Grace", 95));
console.log(JSON.stringify(people[2]));
import { readFileSync } from "node:fs";
const text = readFileSync(0, "utf8");
// 1. parse the records
// 2. log each one: Alan: 85
// 3. log the best: Best: Ada
const ada = { name: "Ada", tags: ["math"] };
const copy = { ...ada };
copy.tags.push("code");
console.log(ada.tags.join(", "));
console.log(copy.tags.join(", "));