Arrays and Objects Problems
These problems put map, filter, reduce, sorting, and object transformations to work on realistic data — shopping carts, student results, and API-style responses. Solve each one before peeking at the solution.
1. Cart Total with Discount
Rizwan's e-commerce store gives a 10% discount when the cart total exceeds Rs 5000. Write checkout(items) that returns the final amount to pay.
const cart = [
{ name: "Mechanical Keyboard", price: 3500, quantity: 1 },
{ name: "Wireless Mouse", price: 1200, quantity: 2 },
{ name: "USB-C Cable", price: 450, quantity: 2 },
];
checkout(cart); // 6120
Solution
const checkout = (items) => {
const total = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
return total > 5000 ? total * 0.9 : total;
};
reduce collapses the array into a single running total: 3500 + 2400 + 900 = 6800. Since 6800 exceeds 5000, the 10% discount applies and the function returns 6120. Always pass the initial value 0 to reduce — without it, an empty cart would throw.
2. Group Students by Grade
Given an array of students, group their names by grade into an object.
const students = [
{ name: "Rizwan", grade: "A" },
{ name: "Hafsa", grade: "A" },
{ name: "Ibrahim", grade: "B" },
{ name: "Zakariya", grade: "C" },
{ name: "Ayesha", grade: "B" },
];
groupByGrade(students);
// { A: ["Rizwan", "Hafsa"], B: ["Ibrahim", "Ayesha"], C: ["Zakariya"] }
Solution
const groupByGrade = (students) =>
students.reduce((groups, student) => {
(groups[student.grade] ??= []).push(student.name);
return groups;
}, {});
The accumulator starts as an empty object. For each student, ??= creates the array for that grade the first time it appears, then we push the name. One pass, O(n). Newer runtimes also offer Object.groupBy(students, (s) => s.grade), which returns whole student objects grouped by grade.
3. Word Frequency Count
Given an array of tags from blog posts, count how many times each tag appears.
const tags = ["api", "db", "api", "cache", "db", "api"];
countTags(tags); // { api: 3, db: 2, cache: 1 }
Solution
const countTags = (tags) =>
tags.reduce((counts, tag) => {
counts[tag] = (counts[tag] ?? 0) + 1;
return counts;
}, {});
This is the classic frequency-map pattern. counts[tag] ?? 0 handles the first occurrence, where the key does not exist yet. You will reuse this pattern constantly — counting votes, log levels, error codes, anything.
4. Remove Duplicates
Ayesha's newsletter service accidentally stored subscriber IDs multiple times. Write unique(ids) that removes duplicates while preserving the first-seen order.
unique([3, 7, 3, 1, 7, 9]); // [3, 7, 1, 9]
Solution
const unique = (ids) => [...new Set(ids)];
A Set only stores unique values and remembers insertion order, so spreading it back into an array is a one-line dedup. For arrays of objects, dedupe by a key instead: build a Map keyed by item.id and spread map.values().
5. Flatten a Nested Array
An API returns category IDs as arbitrarily nested arrays. Write flatten(arr) that produces a single flat array.
flatten([1, [2, 3], [4, [5, [6]]]]); // [1, 2, 3, 4, 5, 6]
Solution
const flatten = (arr) =>
arr.reduce(
(flat, item) =>
flat.concat(Array.isArray(item) ? flatten(item) : item),
[]
);
Each element is either a plain value (append it) or another array (flatten it recursively first). In real code, prefer the built-in arr.flat(Infinity) — but writing the recursive version yourself is a great exercise in recursion plus reduce.
6. Sort Products by Price, Then Name
Sort a product list by price ascending. When two products cost the same, sort them alphabetically by name.
const products = [
{ name: "Laptop Stand", price: 4500 },
{ name: "Wireless Mouse", price: 1200 },
{ name: "Monitor", price: 32000 },
{ name: "Keyboard", price: 1200 },
];
sortProducts(products);
// Keyboard (1200), Wireless Mouse (1200), Laptop Stand (4500), Monitor (32000)
Solution
const sortProducts = (products) =>
[...products].sort(
(a, b) => a.price - b.price || a.name.localeCompare(b.name)
);
The comparator first compares prices; when they are equal, a.price - b.price is 0 (falsy), so || falls through to the name comparison. Spreading into a new array first matters — sort mutates in place, and silently reordering the caller's array is a classic source of bugs.
7. Array to Lookup Object
Your app fetches users as an array, but you constantly need to find a user by ID. Write indexById(users) that converts the array into a lookup object keyed by id.
const users = [
{ id: 101, name: "Rizwan", role: "admin" },
{ id: 102, name: "Hafsa", role: "editor" },
{ id: 103, name: "Ibrahim", role: "viewer" },
];
const byId = indexById(users);
byId[102].name; // "Hafsa"
Solution
const indexById = (users) =>
Object.fromEntries(users.map((user) => [user.id, user]));
map turns each user into a [key, value] pair, and Object.fromEntries assembles the pairs into an object. Building the index costs O(n) once, and every lookup after that is O(1) instead of scanning the array with find each time.
8. Passing Students, Ranked
From exam results, get the names of students who scored at least 50 marks, ordered from highest to lowest score.
const results = [
{ name: "Rizwan", marks: 91 },
{ name: "Ayesha", marks: 45 },
{ name: "Zakariya", marks: 78 },
{ name: "Hafsa", marks: 85 },
];
rankPassing(results); // ["Rizwan", "Hafsa", "Zakariya"]
Solution
const rankPassing = (results) =>
results
.filter((student) => student.marks >= 50)
.sort((a, b) => b.marks - a.marks)
.map((student) => student.name);
A three-step pipeline: filter keeps the passers, sort with b.marks - a.marks orders them descending, and map extracts just the names. Sorting the result of filter is safe here because filter already returned a fresh array — the original results stays untouched.