# Exercise: Pet Adoption Profiles

Finish five functions for a pet adoption site, building profile lines with destructuring and safe copies with spread, and let the tests check each one.

Apprentice · JavaScript · Updated October 30, 2026

Happy Tails Rescue is putting its pets online, and the volunteers need the code behind each pet's profile: a one-line summary for the listing page, a way to show only the dogs (or only the cats), and safe ways to update a pet. "Safe" is the important word. When Mochi the cat is adopted, the adoption papers need a copy that says so, while the listing keeps the original until the paperwork is done. Every task is a function for you to finish, and tests check each one as you go.

## Before you start

You need Node.js, version 24 or later. In a terminal, `node --version` shows the version you have. Nothing else needs installing: the tests use the test runner that comes with Node.

## Get the exercise code

In the **Exercise** box beside the lesson, choose **Get the starter on GitHub** to open its GitHub repository. Click **Code**, then **Download ZIP**, and unzip it into your `objects-practice` folder (or any folder you like). The folder holds:

- `pets.js`: the five functions you'll finish.
- `listing.js`: prints the shelter's listing with your functions, so you can watch them work. You don't need to change it.
- `pets.test.js`: the tests that check your functions. You don't need to change these.
- `package.json`: tells Node that these files share functions with `import` and `export`. You don't need to change it.

Open the folder in VS Code, and open a terminal there with **Terminal > New Terminal**.

## What to do

Every function is already written down with its name and parameters, and returns an empty value for now: `''`, `[]` or `{}`. The comment above it says what it should return, with examples:

```javascript
/*
 * Return a copy of the pet with adopted set to true. The pet you're given stays as it is,
 * because the listing still needs it until the paperwork is done.
 *
 * markAdopted({ name: 'Mochi', species: 'cat', adopted: false })
 *   -> { name: 'Mochi', species: 'cat', adopted: true }
 */
export function markAdopted(pet) {
  return {};
}
```

1. **`profileLine(pet)`**: one line for the listing, such as `Biscuit (dog, 3 years): loves fetch, good with kids`. Unpack the pet with destructuring. A pet aged 1 is `1 year`, and a pet with no traits shows `no traits listed yet`.
2. **`petsOfSpecies(pets, species)`**: a new array with only the pets of one species, in their order.
3. **`markAdopted(pet)`**: a copy of the pet with `adopted: true`.
4. **`mergeDetails(pet, updates)`**: a copy of the pet with the updated details merged in, such as Juniper's new age after her birthday.
5. **`addTrait(pet, trait)`**: a copy of the pet with one more trait at the end of its list.

The rule for all five: **none of them changes what it's given.** Make a copy, then change the copy.

## Check your work

In the terminal, in the exercise folder:

```bash
node --test
```

At first all 11 tests fail. That's expected: each function you finish turns its tests green. When a test fails, its message says what it expected, for example *The pet you were given still says adopted: false. Make a copy, then change the copy.*

To see your functions at work, run the listing:

```bash
node listing.js
```

## Hints

- **Start with the examples.** Each function's comment shows what goes in and what comes out. Work one out by hand before you write code.
- **A default fills a gap.** A pet with no `traits` property still needs a list. *Destructuring* shows how a default covers a missing property. To turn the list into text, `traits.join(', ')` glues the items together with a comma and a space between them (*String Methods*, in chapter 4, covers `join` fully).
- **"Later wins" does the merging.** In *Spread and Rest*, a property written after a spread replaces the copied one. That's all `markAdopted` and `mergeDetails` need.
- **Watch the inner list.** A spread copy of a pet shares its `traits` array with the original. If the original's traits change when you add one, copy the array too.
- **Loop, check, collect.** `petsOfSpecies` is the loop from *Arrays of Objects*: start with an empty array and `push` each match.

## When you're done

Every test is green, and `node listing.js` prints:

```text
Happy Tails Rescue: waiting for a home
  Biscuit (dog, 3 years): loves fetch, good with kids
  Mochi (cat, 2 years): lap cat, chatty
  Pepper (rabbit, 1 year): shy
  Juniper (dog, 7 years): calm, house-trained
  Clover (guinea pig, 2 years): no traits listed yet
Dogs: Biscuit, Juniper
Mochi's adoption papers say adopted: true. The listing still says false.
After her birthday: Juniper (dog, 8 years): calm, house-trained (nickname June)
New note: Biscuit (dog, 3 years): loves fetch, good with kids, snores
The listing still has: Biscuit (dog, 3 years): loves fetch, good with kids
```

Look at the last two lines. Biscuit has a new trait in the copy and the old list in the listing. What would have happened to the last line if `addTrait` had pushed onto the pet's own `traits` array?

## So what

Copy, then change: that's how real apps update data without breaking the screen that still shows the old version, and it's the habit frameworks such as React expect from you.

## Key terms

- [Object Destructuring](https://www.ask-codey.com/glossary/object-destructuring)
- [Spread Syntax](https://www.ask-codey.com/glossary/spread-syntax)
- [Shallow Copy](https://www.ask-codey.com/glossary/shallow-copy-javascript)
- [Immutability](https://www.ask-codey.com/glossary/immutability)

## Related

- [What Is an Object](https://www.ask-codey.com/lessons/javascript-objects-what-is-an-object)
- [Arrays of Objects](https://www.ask-codey.com/lessons/javascript-objects-arrays-of-objects)
- [Destructuring](https://www.ask-codey.com/lessons/javascript-unpacking-destructuring)
- [Spread and Rest](https://www.ask-codey.com/lessons/javascript-unpacking-spread-and-rest)

Source: https://www.ask-codey.com/lessons/exercise-javascript-pet-adoption-profiles
