JS30. Задание 18 Tally String Times


Несложное упражнение. Есть список видео, у каждого видео в data-time атрибуте указана его продолжительность в минутах и секундах в формате: mm:ss. Нужно подсчитать общую продолжительность видео и вывести его в консоль.

Это задание я, пожалуй, могу выполнить и без подсказок автора и, возможно, даже без Google.

Моё решение

var videos = document.querySelectorAll(".videos li");
    var arr = [];
    for(var i = 0; i < videos.length; i++) {
        var videoTime = videos[i].dataset.time;
        arr.push(videoTime);
        }
    var arrTime = [];
    for(var j = 0; j < videos.length; j++) {
        arr[j] = arr[j].split(":");
        var time = arr[j][0] * 60 + +arr[j][1];
        arrTime.push(time);
    }
    var t = arrTime.reduce((a, b) => a + b);
    var seconds = t % 60;
    var mins = (t - seconds) % 3600 / 60;
    var hours = (t - seconds - mins * 60) / 3600;
   
    console.log(hours, mins, seconds)


Решение автора

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);


У автора лучше )

Сравниваю подходы

1. Я получаю объект NodeList. автор использует метод Array.from(), позволяющий получить массив из массивоподобного или итерируемого объекта
Зачем ему понадобился массив? Ведь перебор свойств методом forEach доступен и в 
NodeList тоже. Но доступен ли для NodeList метод map? Нет, не доступен. Так что, если мы хотим применить к полученному объекту метод map (а мы хотим), нужно преобразовать NodeList в массив.

2. Интересная запись - метод map с точкой вначале пишется с новой строки. Аккуратность записи это повышает - мы избавляемся от длинных строк, понятность, как мне кажется, - нет.
В любом случае возможность переосить на новую строку часть выражения отделённую точкой нужно взять на заметку.

3. timeNodes.map(node => node.dataset.time) - эта строка автора аналог моего фрагмента кода

 var arr = [];
    for(var i = 0; i < videos.length; i++) {
        var videoTime = videos[i].dataset.time;
        arr.push(videoTime);
        }


Безусловно, вариант автора лаконичнее удобнее и правильнее.
Почему у меня метод map() не сработал теперь понятно - я пыталась применить его к объекту, не преобразовав его в массив.

4. Авторский вариант
 .map(timeCode => {
      const [mins, secs] = timeCode.split(':').map(parseFloat);
      return (mins * 60) + secs;
    })

Мой:
var arrTime = [];
    for(var j = 0; j < videos.length; j++) {
        arr[j] = arr[j].split(":");
        var time = arr[j][0] * 60 + +arr[j][1];
        arrTime.push(time);
    }


Здесь даже не знаю.
Метод map мне безусловно нравится. Но функцию parseFloat прекрасно заменяет унарный плюс, приводящий строку к числу, а вместо создания массива для минут и секунд я бы использовала индексы
Мой вариант выглядит так. Он чуть проще варианта автора. И он работает

.map(timeCode => {
      timeCode = timeCode.split(':');
      return timeCode[0] * 60 + +timeCode[1];

    })


5. Редукция массива. Хоть что-то, что я умею
Вариант автора

.reduce((total, vidSeconds) => total + vidSeconds);

Мой:

var t = arrTime.reduce((a, b) => a + b);
 
Разница только в именах переменных и в том, что я результат присваиваю новой переменной, а автор продолжает цепочку преобразований.
Для меня мой вариант как минимум не хуже авторского.

6. Преобразование секунд в часы минуты и секунды
Вариант автора

    let secondsLeft = seconds;
    const hours = Math.floor(secondsLeft / 3600);
    secondsLeft = secondsLeft % 3600;

    const mins = Math.floor(secondsLeft / 60);
    secondsLeft = secondsLeft % 60;



Мой:

    var seconds = t % 60;
    var mins = (t - seconds) % 3600 / 60;
    var hours = (t - seconds - mins * 60) / 3600;

 
У меня лучше.

Окончательный вариант кода:

var videos = document.querySelectorAll(".videos li");
var videoArr =
Array.from(videos);
var time = videoArr    
    .map(video => video.dataset.time)
   
.map(timeCode => {
      timeCode = timeCode.split(':');
      return timeCode[0] * 60 + +timeCode[1];

    })

    .reduce((a, b) => a + b);


    var seconds =
time % 60;
    var mins = (
time - seconds) % 3600 / 60;
    var hours = (
time - seconds - mins * 60) / 3600;
   
    console.log(hours, mins, seconds)