1

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?

1
  • try return accumulator; at the end of reducerForTime function Commented Jul 30, 2019 at 16:29

1 Answer 1

2

the accumulator should be kept from one iteration to the next

The accumulator is equal to whatever you returned on the previous iteration. You're not currently returning anything in reducerTime. Change it to:

const reducerForTime = (accumulator, currentValue = new Time()) => {
  accumulator.add(currentValue);
  return accumulator;
};
Sign up to request clarification or add additional context in comments.

1 Comment

Of course! It's obvious now that I see it. The reducer is defined just like any other function and thus needs a return value. Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.