I'm trying to complete an exercise on JavaScript30. I would like to know why the solution I came up with doesn't work.
The idea is simple. You have a web page in which the duration of videos is stored in a list, like so:
<ul class="videos">
<li data-time="5:43">
Video 1
</li>
<li data-time="2:33">
Video 2
</li>
</ul>
There are actually 58 items in the list. The exercise is to tally the total duration of all videos put together (in other words, get the sum of all the durations contained in the list).
I have the code for the course maker's solution and it takes a different path from my solution. It stores the durations as seconds while I created a "Time" class. I understand this solution, but I would like to understand why my solution doesn't work.
Solution provided with the course:
const timeNodes = Array.from(document.querySelectorAll('[data-time]'));
const seconds = timeNodes
.map(node => node.dataset.time)
.map(timeCode => {
const [mins, secs] = timeCode.split(':').map(parseFloat);
return (mins * 60) + secs;
})
.reduce((total, vidSeconds) => total + vidSeconds);
let secondsLeft = seconds;
const hours = Math.floor(secondsLeft / 3600);
secondsLeft = secondsLeft % 3600;
const mins = Math.floor(secondsLeft / 60);
secondsLeft = secondsLeft % 60;
console.log(hours, mins, secondsLeft);
My (not yet functioning) solution:
function Time(hours = 0, minutes = 0, seconds = 0) {
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
this.addTime = function(hours = 0, minutes = 0, seconds = 0) {
const ajout = new Time(hours, minutes, seconds);
this.add(ajout);
}
this.add = function(ajout) {
let surplus = 0;
// seconds
this.seconds += ajout.seconds;
while (this.seconds >= 60) {
this.seconds -= 60;
surplus++;
}
// minutes
this.minutes += ajout.minutes + surplus;
surplus = 0;
while (this.minutes >= 60) {
this.minutes -= 60;
surplus++;
}
// hours
this.hours += ajout.hours + surplus;
}
this.toString = function() {
return `${this.hours} h ${this.minutes} min ${this.seconds} s`;
}
}
function stringToTime (str) {
let h, m, s = 0;
const arr = str.split(':');
while (arr.length > 3) {
arr.shift();
}
if (arr.length == 3) {
h = parseInt(arr.shift());
}
if (arr.length == 2) {
m = parseInt(arr.shift());
}
if (arr.length == 1) {
s = parseInt(arr.shift());
}
return new Time(h, m, s);
}
//const reducerForTime = (accumulator, currentValue = new Time()) => accumulator.add(currentValue) || new ();
const reducerForTime = (accumulator, currentValue = new Time()) =>
{
console.log(currentValue);
//console.log(`accumulator = ${accumulator.toString()}; currentValue = ${currentValue.toString()}`);
accumulator.add(currentValue);
console.log(`accumulator after adding = ${accumulator.toString()}`);
};
const videos = document.querySelectorAll('ul.videos li');
let videoTimes = [];
for (let i = 0; i < videos.length; i++) {
videoTimes.push(stringToTime(videos[i].dataset.time));
}
console.log(videoTimes.reduce(reducerForTime).toString());
The error is the following: Uncaught TypeError: Cannot read property 'add' of undefined
You will notice that I added two "console.log" lines to the reducer function to try and figure out where it went wrong.
I now get this console output:
Time {hours: 0, minutes: 2, seconds: 33, addTime: ƒ, add: ƒ, …}
accumulator after adding = 0 h 8 min 16 s
Time {hours: 0, minutes: 3, seconds: 45, addTime: ƒ, add: ƒ, …}
Uncaught TypeError: Cannot read property 'add' of undefined
at reducerForTime (index-START.html:239)
at Array.reduce (<anonymous>)
at index-START.html:248
reducerForTime @ index-START.html:239
(anonymous) @ index-START.html:248
The lines it refers to are 248 and 239. Line 248 is the one making the call to the reduce function. Line 239 is the one with accumulator.add(currentValue);. I don't know why is doesn't work, because from my understanding, the accumulator should be kept from one iteration to the next, and therefore should be a Time object, and therefore should have an 'add' property. Why is the error message telling me it's undefined?
return accumulator;at the end ofreducerForTimefunction