Arrays and Objects
Table of Contents
- Two Structures for Almost Everything
- Objects
- Arrays
- Dara's Projects as Data
- Transforming Arrays: map, filter, find
- Checking and Combining: some, every, reduce, sort
- Destructuring, Spread, and Rest
- Optional Chaining and Object Helpers
- JSON
- Copy vs. Reference
- Try It Yourself
1. Two Structures for Almost Everything
You now have single values (strings, numbers, booleans). Real programs work with collections of them, and JavaScript has two main tools:
| Structure | Use it for | Looks like |
|---|---|---|
| Array | An ordered list of things | ["HTML", "CSS"] |
| Object | One thing described by named properties | { name: "Dara", city: "Phnom Penh" } |
Most data you meet, from a web API response to a database row, is arrays and objects nested inside each other. If you can read and shape these two, you can handle nearly any data.
2. Objects
An object is a set of key: value pairs inside curly braces:
const dara = {
name: "Dara Sok",
city: "Phnom Penh",
skills: ["HTML", "CSS"],
};
Read a property with a dot, or with square brackets when the key is in a variable or is not a valid name:
console.log(dara.name); // "Dara Sok"
console.log(dara["city"]); // "Phnom Penh"
const key = "name";
console.log(dara[key]); // "Dara Sok"
You can change, add, and remove properties. This works even on a
const object, because const stops you from reassigning the
variable, not from changing what is inside:
dara.city = "Siem Reap"; // change
dara.available = true; // add
delete dara.available; // remove
3. Arrays
An array is an ordered list. You met it in the loops chapter; here are the everyday operations:
const skills = ["HTML", "CSS"];
skills.push("JavaScript"); // add to the end
skills.pop(); // remove from the end, returns "JavaScript"
skills.unshift("Git"); // add to the start
skills.shift(); // remove from the start, returns "Git"
console.log(skills.includes("CSS")); // true
console.log(skills.indexOf("CSS")); // 1
console.log(skills.slice(0, 1)); // [ 'HTML' ] (a copy of part)
console.log(skills.join(" / ")); // "HTML / CSS"
Note that push, pop, shift, and unshift change the array
itself. This is called mutation, and it matters in section 10.
slice, includes, and join do not change it.
4. Dara's Projects as Data
Now combine both. Each project is an object, and the list of
projects is an array of objects. This replaces the four <article>
elements written by hand in the HTML & CSS series: the data lives in
one place, and the page can be built from it.
Create projects.js:
const projects = [
{ id: 1, title: "Recipe Finder", year: 2024, stars: 12, tags: ["html", "css"] },
{ id: 2, title: "Weather Dashboard", year: 2025, stars: 30, tags: ["javascript", "api"] },
{ id: 3, title: "Khmer Typing Game", year: 2025, stars: 8, tags: ["javascript", "css"] },
{ id: 4, title: "Budget Tracker", year: 2023, stars: 21, tags: ["javascript"] },
];
Every object has the same shape: id, title, year, stars, and
a tags array. Keeping a consistent shape is what makes the code that
follows simple. In chapter 11, TypeScript will let you write that shape
down and have the computer check it.
The examples below all use this projects array.
5. Transforming Arrays: map, filter, find
These methods replace most loops. Each takes a callback (chapter 5) and returns a new result without changing the original.
map turns every item into something else. The result has the
same length:
const titles = projects.map((project) => project.title);
console.log(titles);
// [ 'Recipe Finder', 'Weather Dashboard', 'Khmer Typing Game', 'Budget Tracker' ]
filter keeps only the items for which the callback returns true:
const jsTitles = projects
.filter((project) => project.tags.includes("javascript"))
.map((project) => project.title);
console.log(jsTitles);
// [ 'Weather Dashboard', 'Khmer Typing Game', 'Budget Tracker' ]
find returns the first matching item, or undefined if none
matches:
console.log(projects.find((project) => project.id === 3)?.title);
// "Khmer Typing Game"
console.log(projects.find((project) => project.id === 99));
// undefined
Chaining filter then map, as above, is a pattern you will use all
the time. It is exactly how the filterable project list on Dara's page
will pick its items.
6. Checking and Combining: some, every, reduce, sort
some asks "does at least one item match?" and every asks
"do all of them match?". Both return a boolean:
console.log(projects.some((project) => project.stars > 25)); // true
console.log(projects.every((project) => project.year >= 2024)); // false
reduce combines all items into one value. It receives a running
accumulator and the current item, and starts from the value you give
as the second argument:
const totalStars = projects.reduce((sum, project) => sum + project.stars, 0);
console.log(totalStars); // 71
sort puts items in order. Be careful: it mutates the array,
and without a comparison function it sorts as text:
console.log([10, 9, 1].sort()); // [ 1, 10, 9 ] (text order!)
console.log([10, 9, 1].sort((a, b) => a - b)); // [ 1, 9, 10 ]
The comparison function returns a negative number if a comes first, a
positive number if b comes first, and zero if they are equal. To sort
without changing the original, copy it first with spread (next section):
const byStars = [...projects].sort((a, b) => b.stars - a.stars);
console.log(byStars.map((project) => project.title));
// [ 'Weather Dashboard', 'Budget Tracker', 'Recipe Finder', 'Khmer Typing Game' ]
For sorting words, use a.localeCompare(b) instead of subtraction.
| Method | Returns | Changes the original? |
|---|---|---|
map | New array, same length | No |
filter | New array, shorter or equal | No |
find | One item or undefined | No |
some / every | true or false | No |
reduce | One value of any type | No |
sort | The same array, reordered | Yes |
7. Destructuring, Spread, and Rest
These three features use ... and { } / [ ] to save typing.
Destructuring pulls values out of an object or array into variables:
const { title, year } = projects[0];
console.log(title, year); // "Recipe Finder" 2024
const { stars: starCount, license = "MIT" } = projects[0];
console.log(starCount, license); // 12 "MIT"
const [first, second, ...others] = projects;
console.log(first.id, second.id, others.length); // 1 2 2
You can rename a property (stars: starCount) and give a default
(license = "MIT"). It works in function parameters too, which is very
common:
const printCard = ({ title, year }) => `${title} (${year})`;
console.log(printCard(projects[1])); // "Weather Dashboard (2025)"
Spread (...) copies the items or properties of one structure into
a new one:
const updated = { ...projects[0], stars: 13 };
console.log(updated.stars, projects[0].stars); // 13 12
const more = [...projects, { id: 5, title: "New", year: 2026, stars: 0, tags: [] }];
console.log(more.length, projects.length); // 5 4
Properties written later win, so stars: 13 replaces the copied value.
This is how you "change" data without mutating it: build a new object.
Rest uses the same ... on the other side, to collect what is left:
const { tags, ...rest } = projects[0];
console.log(rest); // { id: 1, title: 'Recipe Finder', year: 2024, stars: 12 }
A way to remember: spread spreads out a structure into pieces; rest gathers pieces into a structure.
8. Optional Chaining and Object Helpers
Reading a property of undefined or null throws an error. Optional
chaining (?.) stops and gives undefined instead. Combine it with
?? (from chapter 3) for a fallback value:
const user = { profile: { address: null } };
console.log(user.profile?.address?.city); // undefined
console.log(user.settings?.theme ?? "light"); // "light"
Three helpers list an object's contents:
console.log(Object.keys(dara)); // [ 'name', 'city', 'skills' ]
console.log(Object.values(dara).length); // 3
for (const [key, value] of Object.entries({ html: 1, css: 2 })) {
console.log(key, value);
}
// html 1
// css 2
Object.entries returns [key, value] pairs, which pair nicely with
for...of and destructuring. This is the safe replacement for
for...in that chapter 4 recommended.
One more useful tool: count the tags across all projects, to build the filter buttons later.
const allTags = [...new Set(projects.flatMap((project) => project.tags))];
console.log(allTags); // [ 'html', 'css', 'javascript', 'api' ]
flatMap joins every project's tags array into one list, and a Set
removes duplicates.
9. JSON
JSON (JavaScript Object Notation) is a text format for data. Almost
every web API sends and receives JSON, and it looks like an object
literal with stricter rules: keys must use double quotes, and values can
only be strings, numbers, booleans, null, arrays, or objects. No
functions, no undefined.
Two functions convert between JavaScript values and JSON text:
const json = JSON.stringify(projects[0]);
console.log(json);
// {"id":1,"title":"Recipe Finder","year":2024,"stars":12,"tags":["html","css"]}
const back = JSON.parse(json);
console.log(back.tags); // [ 'html', 'css' ]
JSON.stringify(value, null, 2) adds indentation, which is easier to
read. Remember these two directions:
stringify: JavaScript value to text (to send or save).parse: text to JavaScript value (after you receive it). If the text is not valid JSON,parsethrows an error; chapter 10 shows how to handle that.
In chapter 9, the GitHub API will send Dara's repositories as JSON,
and fetch will parse it for you.
10. Copy vs. Reference
Numbers and strings are copied when you assign them. Arrays and objects are not: the variable holds a reference, like an address, to the data.
const a = { n: 1 };
const b = a; // b points to the SAME object
b.n = 2;
console.log(a.n); // 2
Changing b changed a, because they are one object. This also means
two separate objects are never equal, even with identical contents:
console.log({} === {}); // false
console.log([1] === [1]); // false
Spread makes a shallow copy: the top level is new, but nested objects and arrays are still shared.
const copy = { ...projects[0] };
copy.tags.push("x"); // changes the nested array
console.log(projects[0].tags); // [ 'html', 'css', 'x' ]
To copy everything, including nested data, use structuredClone:
projects[0].tags.pop(); // undo the accidental change
const deep = structuredClone(projects[0]);
deep.tags.push("y");
console.log(projects[0].tags); // [ 'html', 'css' ]
console.log(deep.tags); // [ 'html', 'css', 'y' ]
Practical advice: prefer methods that return new values (map,
filter, spread) over ones that mutate (push, sort), especially for
data that the page displays. It makes bugs much easier to find.
11. Try It Yourself
- Build your own list. Create an object describing yourself with a
name, acity, and askillsarray. Print each skill withfor...of, then add a skill withpush. - Add a project. Add a fifth project to the
projectsarray with your own title and tags. Which of the methods in section 5 still work without any change? - Filter and sort. Show the titles of projects from 2025 or later,
sorted by stars from highest to lowest, without changing
projects. - Find the average. Use
reduceto compute the average number of stars. Then usesometo check whether any project has zero stars. - Destructure. Write a function
summary({ title, stars, tags })that returns a string likeWeather Dashboard: 30 stars, 2 tags. - JSON round trip. Turn
projectsinto JSON text, then parse it back. Is the result===to the original array? Explain why or why not. Then tryJSON.parse("{ title: 'x' }")and read the error.
Next, we'll split code into separate files with modules, and meet Node and npm, the tools that run and manage your JavaScript projects.
Buy Me a Coffee
Found this tutorial helpful? Consider buying me a coffee to support more free content!