Arrays and Objects

Table of Contents

  1. Two Structures for Almost Everything
  2. Objects
  3. Arrays
  4. Dara's Projects as Data
  5. Transforming Arrays: map, filter, find
  6. Checking and Combining: some, every, reduce, sort
  7. Destructuring, Spread, and Rest
  8. Optional Chaining and Object Helpers
  9. JSON
  10. Copy vs. Reference
  11. 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:

StructureUse it forLooks like
ArrayAn ordered list of things["HTML", "CSS"]
ObjectOne 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.

MethodReturnsChanges the original?
mapNew array, same lengthNo
filterNew array, shorter or equalNo
findOne item or undefinedNo
some / everytrue or falseNo
reduceOne value of any typeNo
sortThe same array, reorderedYes

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, parse throws 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

  1. Build your own list. Create an object describing yourself with a name, a city, and a skills array. Print each skill with for...of, then add a skill with push.
  2. Add a project. Add a fifth project to the projects array with your own title and tags. Which of the methods in section 5 still work without any change?
  3. Filter and sort. Show the titles of projects from 2025 or later, sorted by stars from highest to lowest, without changing projects.
  4. Find the average. Use reduce to compute the average number of stars. Then use some to check whether any project has zero stars.
  5. Destructure. Write a function summary({ title, stars, tags }) that returns a string like Weather Dashboard: 30 stars, 2 tags.
  6. JSON round trip. Turn projects into JSON text, then parse it back. Is the result === to the original array? Explain why or why not. Then try JSON.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!