Episode 1 — Fundamentals / 1.23 — Objects
1.23.g — Practice Problems
In one sentence: Eight hands-on coding problems that reinforce every concept from
1.23.a–1.23.f— from building user profiles to comparing objects and inverting key-value pairs.
Navigation: ← 1.23 Overview · 1.23-Exercise-Questions →
How to use this material
- Read the problem statement and try to solve it yourself before looking at hints.
- Write actual code — do not just read the solution.
- Test edge cases — empty objects, missing properties, different types.
- Compare your solution with the provided one — there are often multiple valid approaches.
Problem 1: User profile system (CRUD)
Problem statement
Create a userProfile object with properties name, email, age, and bio. Then write four operations:
- Create — build the initial object.
- Read — write a function
getField(user, fieldName)that safely returns the value or"Field not found". - Update — write a function
updateField(user, fieldName, newValue)that updates a field only if it already exists. - Delete — write a function
removeField(user, fieldName)that removes a field and returns a new object (immutable).
Hints
- Use bracket notation for dynamic field access.
- Use
Object.hasOwnto check existence. - Use destructuring + rest for immutable delete.
Solution
// CREATE
const userProfile = {
name: "Alice",
email: "alice@example.com",
age: 25,
bio: "Full-stack developer",
};
// READ
function getField(user, fieldName) {
if (Object.hasOwn(user, fieldName)) {
return user[fieldName];
}
return "Field not found";
}
console.log(getField(userProfile, "name")); // "Alice"
console.log(getField(userProfile, "phone")); // "Field not found"
// UPDATE (mutable)
function updateField(user, fieldName, newValue) {
if (Object.hasOwn(user, fieldName)) {
user[fieldName] = newValue;
return true;
}
return false; // field doesn't exist — no update
}
updateField(userProfile, "age", 26);
console.log(userProfile.age); // 26
updateField(userProfile, "phone", "555-0123"); // returns false — no "phone" field
console.log(userProfile.phone); // undefined — was not added
// DELETE (immutable)
function removeField(user, fieldName) {
const { [fieldName]: removed, ...rest } = user;
return rest;
}
const withoutBio = removeField(userProfile, "bio");
console.log(withoutBio); // { name: "Alice", email: "alice@example.com", age: 26 }
console.log(userProfile.bio); // "Full-stack developer" — original unchanged
Explanation
- Dynamic destructuring
{ [fieldName]: removed, ...rest }extracts the named field intoremovedand collects everything else intorest. - The original object is never mutated by
removeFieldbecause spread creates a new object.
Problem 2: Shopping cart object
Problem statement
Create a shopping cart object with:
- An
itemsarray (each item hasname,price,quantity) - An
addItem(name, price, quantity)method - A
removeItem(name)method - A
getTotal()method that calculates the total price - A
getSummary()method that returns a formatted string
Hints
- Use
findIndexto locate items by name. getTotalshould multiply price by quantity for each item, then sum.
Solution
const cart = {
items: [],
addItem(name, price, quantity = 1) {
const existing = this.items.find(item => item.name === name);
if (existing) {
existing.quantity += quantity;
} else {
this.items.push({ name, price, quantity });
}
},
removeItem(name) {
const index = this.items.findIndex(item => item.name === name);
if (index !== -1) {
this.items.splice(index, 1);
return true;
}
return false;
},
getTotal() {
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
},
getSummary() {
if (this.items.length === 0) return "Cart is empty.";
const lines = this.items.map(
item => ` ${item.name} x${item.quantity} @ $${item.price.toFixed(2)} = $${(item.price * item.quantity).toFixed(2)}`
);
lines.push(` TOTAL: $${this.getTotal().toFixed(2)}`);
return lines.join("\n");
},
};
// Test
cart.addItem("Widget", 9.99, 2);
cart.addItem("Gadget", 24.99, 1);
cart.addItem("Widget", 9.99, 3); // adds to existing quantity
console.log(cart.getSummary());
// Widget x5 @ $9.99 = $49.95
// Gadget x1 @ $24.99 = $24.99
// TOTAL: $74.94
cart.removeItem("Gadget");
console.log(cart.getTotal()); // 49.95
Explanation
addItemchecks for an existing item by name to avoid duplicates — it increments quantity instead.getTotalusesreduceto accumulateprice * quantityacross all items.thisrefers to thecartobject because the methods are called with dot notation oncart.
Problem 3: Student record with nested grades
Problem statement
Given a student object with nested grades (an object where keys are subjects and values are arrays of scores), write:
getSubjectAverage(student, subject)— average for one subject.getOverallAverage(student)— average across all subjects.getHighestSubject(student)— the subject with the highest average.
Hints
- Use
Object.entriesto iterate over subjects. - Sum an array with
reduce.
Solution
const student = {
name: "Alice",
id: "STU-001",
grades: {
math: [95, 88, 92, 78],
english: [85, 90, 88, 92],
science: [72, 68, 80, 75],
history: [91, 87, 93, 89],
},
};
function getSubjectAverage(student, subject) {
const scores = student.grades?.[subject];
if (!scores || scores.length === 0) return null;
const sum = scores.reduce((total, score) => total + score, 0);
return sum / scores.length;
}
function getOverallAverage(student) {
const allScores = Object.values(student.grades).flat();
if (allScores.length === 0) return null;
const sum = allScores.reduce((total, score) => total + score, 0);
return sum / allScores.length;
}
function getHighestSubject(student) {
let highest = { subject: null, average: -Infinity };
for (const [subject, scores] of Object.entries(student.grades)) {
const avg = getSubjectAverage(student, subject);
if (avg > highest.average) {
highest = { subject, average: avg };
}
}
return highest;
}
// Test
console.log(getSubjectAverage(student, "math")); // 88.25
console.log(getSubjectAverage(student, "science")); // 73.75
console.log(getOverallAverage(student)); // 83.4375
console.log(getHighestSubject(student));
// { subject: "history", average: 90 }
Explanation
Object.values(student.grades).flat()collects all scores into a single flat array.getHighestSubjectiterates entries and tracks the subject with the maximum average.
Problem 4: Merge two objects (shallow)
Problem statement
Write a function mergeObjects(obj1, obj2) that:
- Creates a new object (does not mutate either input).
- Properties from
obj2overrideobj1if keys overlap. - Returns the merged result.
Test with overlapping and non-overlapping keys.
Hints
- Use the spread operator.
Solution
function mergeObjects(obj1, obj2) {
return { ...obj1, ...obj2 };
}
// Test
const defaults = { theme: "light", fontSize: 14, language: "en", showTips: true };
const userPrefs = { theme: "dark", fontSize: 18 };
const merged = mergeObjects(defaults, userPrefs);
console.log(merged);
// { theme: "dark", fontSize: 18, language: "en", showTips: true }
// Originals unchanged
console.log(defaults.theme); // "light"
console.log(userPrefs.language); // undefined
Explanation
- The spread operator copies properties left to right; later properties overwrite earlier ones with the same key.
- Neither input is mutated because
{ ... }creates a new object.
Problem 5: Count properties in an object
Problem statement
Write a function countProperties(obj) that returns:
total— number of own properties.byType— an object counting how many values of each type exist.
Hints
- Use
Object.valuesandtypeof.
Solution
function countProperties(obj) {
const values = Object.values(obj);
const byType = {};
for (const value of values) {
const type = typeof value;
byType[type] = (byType[type] ?? 0) + 1;
}
return {
total: values.length,
byType,
};
}
// Test
const data = {
name: "Alice",
age: 25,
isAdmin: true,
bio: "Developer",
score: 98.5,
greet() { return "hi"; },
address: { city: "Portland" },
tags: ["js", "css"],
nothing: null,
};
console.log(countProperties(data));
// {
// total: 9,
// byType: { string: 2, number: 2, boolean: 1, function: 1, object: 3 }
// }
// Note: null, array, and plain object all have typeof "object"
Explanation
typeof nullis"object",typeof []is"object"— both counted under"object".- The
?? 0pattern initializes the count to 0 if the type key does not exist yet.
Problem 6: Invert keys and values
Problem statement
Write a function invertObject(obj) that swaps keys and values. If a value is not a string or number, skip it. If duplicate values exist, the last key wins.
Hints
- Use
Object.entriesand filter by type.
Solution
function invertObject(obj) {
const result = {};
for (const [key, value] of Object.entries(obj)) {
if (typeof value === "string" || typeof value === "number") {
result[value] = key;
}
}
return result;
}
// Test
const original = { a: 1, b: 2, c: 3, d: "hello", e: true, f: 2 };
console.log(invertObject(original));
// { 1: "a", 2: "f", 3: "c", hello: "d" }
// Note: key "2" is "f" (last wins, since both b and f had value 2)
// Boolean `true` was skipped
Explanation
- Only string and number values become valid keys in the inverted object.
- When multiple keys share the same value, the last one processed overwrites earlier ones.
Problem 7: Compare two objects (shallow equality)
Problem statement
Write a function shallowEqual(obj1, obj2) that returns true if both objects have the same keys with the same values (using === for value comparison).
Hints
- Compare key counts first (early return).
- Then check every key-value pair.
Solution
function shallowEqual(obj1, obj2) {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
// Different number of keys — not equal
if (keys1.length !== keys2.length) return false;
// Check every key in obj1 exists in obj2 with the same value
for (const key of keys1) {
if (!Object.hasOwn(obj2, key) || obj1[key] !== obj2[key]) {
return false;
}
}
return true;
}
// Test
console.log(shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2 })); // true
console.log(shallowEqual({ a: 1, b: 2 }, { b: 2, a: 1 })); // true (order doesn't matter)
console.log(shallowEqual({ a: 1, b: 2 }, { a: 1, b: 3 })); // false (different value)
console.log(shallowEqual({ a: 1, b: 2 }, { a: 1 })); // false (different keys)
console.log(shallowEqual({}, {})); // true
// Limitation — nested objects:
const addr = { city: "Portland" };
console.log(shallowEqual({ a: addr }, { a: addr })); // true (same reference)
console.log(shallowEqual({ a: { x: 1 } }, { a: { x: 1 } })); // false (different references!)
Explanation
- Shallow equality only uses
===on values — nested objects must be the same reference to be considered equal. - The key count check is an optimization: if counts differ, we can immediately return
false. - We check
Object.hasOwn(obj2, key)to ensure obj2 actually has the key (not just an undefined value from a missing key).
Problem 8: Find all keys with a specific value type
Problem statement
Write a function keysByType(obj, targetType) that returns an array of keys whose values match the given typeof string.
Also write keysWithArrayValues(obj) that finds keys where the value is an array (since typeof [] is "object").
Hints
- Use
Array.isArrayfor array detection.
Solution
function keysByType(obj, targetType) {
return Object.entries(obj)
.filter(([, value]) => typeof value === targetType)
.map(([key]) => key);
}
function keysWithArrayValues(obj) {
return Object.entries(obj)
.filter(([, value]) => Array.isArray(value))
.map(([key]) => key);
}
// Test
const data = {
name: "Alice",
age: 25,
isAdmin: true,
scores: [95, 88, 72],
address: { city: "Portland" },
tags: ["js", "css"],
greet() { return "hi"; },
rating: 4.5,
};
console.log(keysByType(data, "string")); // ["name"]
console.log(keysByType(data, "number")); // ["age", "rating"]
console.log(keysByType(data, "boolean")); // ["isAdmin"]
console.log(keysByType(data, "function")); // ["greet"]
console.log(keysByType(data, "object")); // ["scores", "address", "tags"] — includes arrays!
console.log(keysWithArrayValues(data)); // ["scores", "tags"] — only actual arrays
Explanation
typeofcannot distinguish arrays from objects (both return"object"), soArray.isArrayis needed for precise array detection.- The
filter+mappipeline on entries is a clean, declarative pattern.
Summary
| Problem | Core concepts practiced |
|---|---|
| 1. User profile CRUD | Dynamic access, Object.hasOwn, destructuring + rest |
| 2. Shopping cart | Methods, this, reduce, array inside object |
| 3. Nested grades | Object.entries, Object.values, .flat(), nested access |
| 4. Merge objects | Spread operator, immutability |
| 5. Count properties | Object.values, typeof, dynamic key counting |
| 6. Invert keys/values | Object.entries, type filtering, computed keys |
| 7. Shallow equality | Object.keys, reference vs value comparison |
| 8. Keys by type | Object.entries, typeof, Array.isArray |
Navigation: ← 1.23 Overview · 1.23-Exercise-Questions →