Object Methods and Spread
Introduction
The Problem with Objects and map
You know how to use map and filter on arrays. But what happens when your data is an object instead of an array?
const product = {
name: "Wireless Headphones",
price: 79.99,
brand: "SoundCo",
inStock: true,
};
You can't call product.map(...). map only works on arrays.
So how do you process the data inside an object? You convert it to an array first — and JavaScript gives you three built-in tools to do exactly that.
What You'll Learn Today
- How to get an object's keys as an array with
Object.keys() - How to get an object's values as an array with
Object.values() - How to get both at once with
Object.entries() - How to copy and merge objects safely with the spread operator (
...)
Before You Begin
Accept the GitHub Classroom assignment and clone the repository. Follow the README.md to get set up.
Object.keys(): Get the Property Names
Object.keys() returns an array of an object's property names.
const student = {
name: "Alice",
grade: 92,
enrolled: true,
};
const keys = Object.keys(student);
console.log(keys); // ["name", "grade", "enrolled"]
The result is a regular array — so you can use map, filter, or anything else you already know on it.
// How many properties does this object have?
console.log(Object.keys(student).length); // 3
You actually used Object.keys() back in CS Fundamentals 4 to loop over an object's properties. Now you'll use it with map and filter instead.
Practice 1
const product = {
name: "Laptop",
price: 899,
inStock: true,
brand: "TechCo",
};
Use Object.keys() to get all property names. Then use filter to keep only the keys that are longer than 4 characters.
Hint
Strings have a .length property. "price".length is 5. Chain Object.keys() and filter — the callback receives one key at a time.
Solution
const longKeys = Object.keys(product).filter((key) => key.length > 4);
console.log(longKeys); // ["price", "inStock", "brand"]
Object.values(): Get the Property Values
Object.values() returns an array of just the values — no keys.
const student = {
name: "Alice",
grade: 92,
enrolled: true,
};
const values = Object.values(student);
console.log(values); // ["Alice", 92, true]
This is useful when you only care about the data, not what it's called.
Practice 2
const gradeBreakdown = {
homework: 85,
quiz: 62,
midterm: 91,
final: 88,
participation: 95,
};
Use Object.values() and filter to get only the scores that are 80 or above.
Hint
Object.values() gives you an array of just the numbers. From there, filter works exactly like it did in Callbacks and Array Methods — the callback receives one score at a time.
Solution
const passing = Object.values(gradeBreakdown).filter((score) => score >= 80);
console.log(passing); // [85, 91, 88, 95]
Object.entries(): Get Keys and Values Together
Object.entries() returns an array of [key, value] pairs. Each item in the result is a two-element array.
const student = {
name: "Alice",
grade: 92,
enrolled: true,
};
const entries = Object.entries(student);
console.log(entries);
// [
// ["name", "Alice"],
// ["grade", 92],
// ["enrolled", true],
// ]
This is the most powerful of the three because you have access to both the key and the value at the same time. But each item in the result is a two-element array — so before we use entries with map, you need one new tool: array destructuring.
Array Destructuring
When you have an array and want to pull values out into variables, you could do it the manual way:
const pair = ["alice", 95];
const name = pair[0];
const score = pair[1];
Array destructuring does the same thing in one line:
const [name, score] = pair;
console.log(name); // "alice"
console.log(score); // 95
The square brackets on the left mirror the shape of the array on the right. JavaScript assigns each variable to the matching position.
Destructuring in Callbacks
Object.entries() gives you an array of [key, value] pairs. Callbacks receive each item one at a time — so each item your callback receives is itself a two-element array. You can destructure it right in the callback parameter:
const student = { name: "Alice", grade: 92, enrolled: true };
const lines = Object.entries(student).map(([key, value]) => `${key}: ${value}`);
console.log(lines);
// ["name: Alice", "grade: 92", "enrolled: true"]
([key, value]) is array destructuring in the callback parameter. Instead of receiving the pair as a single array and then pulling items out manually, you name both items right in the function signature. Here's the same operation written both ways:
const student = { name: "Alice", grade: 92, enrolled: true };
// Without destructuring — pull values out manually
const lines = Object.entries(student).map((pair) => {
const key = pair[0];
const value = pair[1];
return `${key}: ${value}`;
});
console.log(lines);
// ["name: Alice", "grade: 92", "enrolled: true"]
const student = { name: "Alice", grade: 92, enrolled: true };
// With destructuring — same result, less noise
const lines = Object.entries(student).map(([key, value]) => `${key}: ${value}`);
console.log(lines);
// ["name: Alice", "grade: 92", "enrolled: true"]
Practice 3
const inventory = {
apples: 50,
bananas: 3,
oranges: 0,
grapes: 14,
mangoes: 2,
};
Use Object.entries() and filter to keep only items with fewer than 5 in stock. Then use map to turn each low-stock entry into a warning string like "bananas: only 3 left".
Hint
Chain it: .entries() → .filter(...) → .map(...)
You need both the item name and the count to build the message — that's exactly what entries gives you.
Solution
const warnings = Object.entries(inventory)
.filter(([_, count]) => count < 5)
.map(([item, count]) => `${item}: only ${count} left`);
console.log(warnings);
// ["bananas: only 3 left", "oranges: only 0 left", "mangoes: only 2 left"]
The _ in filter(([_, count]) => ...) is a convention for "I'm not using this value." The item name is still destructured — we just don't need it until the map step. However, we must have something in that position to 'skip over' the first item in the pair and get to the count.
Checkpoint
Write your answers in handwritten notes ✍️.
- What does
Object.keys()return? - What does
Object.values()return? - What does each item in
Object.entries()look like? - You have an object with 5 properties. You call
Object.values()thenfilter. How many items could the result have? - What does
([key, value])mean in a callback parameter? Why the extra parentheses?
Spread: Copying and Merging Objects
You've already used spread with arrays in Callbacks and Array Methods:
// ✅ Creates a new object with spread
const good = students.map((student) => ({
...student,
passing: student.grade >= 70,
}));
That ...student is the spread operator. It copies all properties from one object into a new one. Here's how it works on its own:
Copying an Object
const student = { name: "Alice", grade: 92 };
// ❌ This doesn't copy — both variables point to the same object
const copy = student;
// ✅ Spread creates a new object with the same properties
const spreadCopy = { ...student };
Why does the first version fail? You learned this in CS Fundamentals 1: objects are stored by reference. copy = student just makes a second label pointing to the same data. Change one, you change both. It's like 2️⃣ remote controls that control the same TV 📺.
Spread creates a genuinely new object.
Adding or Overriding Properties
Spread really shines when you want to create a modified copy without touching the original (avoiding mutation). You can add new properties or override existing ones by listing them after the spread.
const student = { name: "Alice", grade: 92 };
// Add a new property
const withStatus = { ...student, enrolled: true };
console.log(withStatus); // { name: "Alice", grade: 92, enrolled: true }
console.log(student); // { name: "Alice", grade: 92 } — unchanged ✅
// Override an existing property
const curved = { ...student, grade: 97 };
console.log(curved); // { name: "Alice", grade: 97 }
console.log(student); // { name: "Alice", grade: 92 } — unchanged ✅
Order matters: properties listed after the spread override ones from it.
Merging Two Objects
const defaults = {
theme: "light",
notifications: true,
itemsPerPage: 10,
};
const userPreferences = {
theme: "dark",
itemsPerPage: 25,
};
const settings = { ...defaults, ...userPreferences };
console.log(settings);
// { theme: "dark", notifications: true, itemsPerPage: 25 }
userPreferences is spread after defaults, so its values win on any overlap. notifications stays from defaults because userPreferences doesn't have it.
Practice 4
const profile = { name: "Alex", level: 3, active: true };
Create a new object promoted that has all the same properties, but with level bumped to 4 and a new property badge set to "Gold". Verify that profile is unchanged.
Hint
Spread profile first, then list the properties you want to add or override after it. To verify the original is unchanged, console.log(profile) after.
Solution
const promoted = { ...profile, level: 4, badge: "Gold" };
console.log(promoted); // { name: "Alex", level: 4, active: true, badge: "Gold" }
console.log(profile); // { name: "Alex", level: 3, active: true } — unchanged ✅
Wrap-Up
Key Takeaways
Object.keys(obj)→ array of property namesObject.values(obj)→ array of property valuesObject.entries(obj)→ array of[key, value]pairs- All three return regular arrays — you can chain
map,filter, and anything else you know - Spread
{ ...obj }creates a new object; it doesn't modify the original - Adding properties after the spread overrides any matching keys from the original.