A report answers a question about data. Our report needs total planned minutes, completed minutes, and remaining minutes. Decide what each number means before naming the variables. A session marked done contributes its whole duration; partial completion is outside this project’s model.
Use a function returning an object of totals. A second function can turn that object into text. This separation lets a webpage display the same numbers without parsing a formatted string. Test the numbers directly, including an empty plan.
When a result is wrong, inspect the input and each stage. Did filtering select the right records? Did the sum start at zero? Did a property name match the records? More clever syntax will not repair an unclear definition.
Predict before running
Only one session is completed. Which minutes count toward planned, and which toward completed?
function summarize(sessions) {
let planned = 0;
let completed = 0;
for (const session of sessions) {
planned = planned + session.minutes;
if (session.done) {
completed = completed + session.minutes;
}
}
return { planned, completed, remaining: planned - completed };
}
const summary = summarize([
{ topic: "Loops", minutes: 25, done: true },
{ topic: "Arrays", minutes: 20, done: false }
]);
console.log(summary.planned);
console.log(summary.completed);
console.log(summary.remaining);Check your prediction
45
25
20Trace each statement in order. If your prediction differed, describe the first place your mental model and the result diverged before trying again.
Make report rules explicit
A report depends on definitions. Does total mean all planned minutes, only completed minutes, or minutes still remaining? Choose names that make this visible. Empty input is not an exceptional situation: a new learner has no records yet. Avoid dividing by zero when calculating an average.
function averageMinutes(sessions) {
if (sessions.length === 0) {
return 0;
}
let total = 0;
for (const session of sessions) {
total += session.minutes;
}
return total / sessions.length;
}
console.log(averageMinutes([]));
console.log(averageMinutes([{ minutes: 20 }, { minutes: 40 }]));Check your prediction
0
30Returning zero for an empty average is a product decision here. Another application might return null and display “No sessions yet”. Document whichever contract you choose.
Calculate each total from its stated rule
A report derives values from source records; it should not change those records while adding them up. Planned minutes include every session. Completed minutes include only sessions marked done. Remaining minutes are their difference, not the number of unfinished records.
Use a tiny fixture where the units are obvious. Two sessions lasting 10 and 40 minutes are useful because confusing a count with a duration produces visibly different answers. Include an empty list and an all-complete list before trusting a report on realistic data.
const sessions = [
{ minutes: 10, done: true },
{ minutes: 40, done: false }
];
let count = 0;
let minutes = 0;
for (const session of sessions) {
if (session.done) {
count += 1;
minutes += session.minutes;
}
}
console.log(count);
console.log(minutes);Check your prediction
1
10 The same condition updates both accumulators, but their units differ. A variable named completed without a unit can hide this distinction. Prefer completedCount or completedMinutes.
const sessions = [
{ minutes: 10, done: true },
{ minutes: 20, done: true }
];
let planned = 0;
let completed = 0;
for (const session of sessions) {
planned += session.minutes;
if (session.done) {
completed += session.minutes;
}
}
console.log(planned - completed);
console.log(sessions[0].done);Check your prediction
0
true A legitimate zero result must not be replaced with a default message by truthiness. The source flag is still true because calculating the report did not need to mutate it.
Try the rule on new inputs
Check empty, none-done, some-done and all-done plans. Choose different durations so a count-based mistake cannot accidentally pass. Explain why toggling a session should change completed minutes but not planned minutes.
Your turn
Reports & Tests: practise
Write completedMinutes(sessions). Test an empty array, an incomplete 20-minute session, and a completed 25-minute session. Return 0, 0, and 25 respectively.
// Write completedMinutes, then throw an error if any expectation fails.Hint 1: the idea
Only add a record’s minutes when its done field is true.
Hint 2: a first step
Begin with zero before the loop. Compare three calls with !== and throw on a mismatch.
Show one solution
function completedMinutes(sessions) {
let total = 0;
for (const session of sessions) {
if (session.done) {
total = total + session.minutes;
}
}
return total;
}
if (completedMinutes([]) !== 0) {
throw new Error("empty");
}
if (completedMinutes([{ minutes: 20, done: false }]) !== 0) {
throw new Error("incomplete");
}
if (completedMinutes([{ minutes: 25, done: true }]) !== 25) {
throw new Error("completed");
}
console.log("3 checks passed");3 checks passedTry deleting the if condition: the incomplete case should catch the defect. You now have a small safety net for changing the implementation.
What you should be able to explain
- What does each number in your report mean?
- What should an empty report display?
- Does your reporting function change any records?
Completion is saved in this browser only. It does not measure mastery.