JavaScript RegExp has named capture groups that can make life a lot simpler when dealing with complicated RegExp. Combined with destructuring assignment you can extract the named hours minutes and seconds as follows.
function toSeconds(time) {
const {groups: {h = 0, m = 0, s = 0}} = /(?<h>\d*)h(?<m>\d*)m(?<s>\d*)/i.exec(time);
return h * 3.6e3 + m * 60 + s * 1; // * 1 to coerce s to Number
}
Missing digits are set to zero in the assignment defaults.
However this is limited to strings that have hours, minutes, and seconds in the correct order (hence no need to match the "s"
) and will throw an error if there is a problem.
A more robust solution
You can also reduce the array created by symbol.matchAll
(it returns an iterator that you convert to an array via spread operator)
It RegExp[symbol.matchAll]
is the same call as String.matchAll(RegExp)
To handle as many variations as possible you can convert the time string to lowercase, soak up white spaces, allow for fractions, multiple periods, and negative periods.
Using an IIF to wrap the periods constant via closure the function looks like
const toSeconds = (() => {
const periods = {h: 3600, m: 60, s: 1};
return time => [.../(\-*\d*\.*\d*)\W*([hms])/g[Symbol.matchAll](time.toLowerCase())]
.reduce((time, [, digits, type]) => periods[type] * digits + time, 0);
})();
Or via the string
const toSeconds = (() => {
const periods = {h: 3600, m: 60, s: 1};
return time => [...time.toLowerCase().matchAll(/(\-*\d*\.*\d*)\W*([hms])/g)]
.reduce((time, [, digits, type]) => periods[type] * digits + time, 0);
})();
To combat the readability the next version creates some extra variables to segregate the logic parts a little
const toSeconds = (() => {
const periods = {h: 3600, m: 60, s: 1};
const extractHMS = /(\-*\d*\.*\d*)\W*([hms])/g;
const sumSeconds = (time, [, digits, type]) => periods[type] * digits + time;
return time => [...time.toLowerCase().matchAll(extractHMS)].reduce(sumSeconds, 0);
})();
The snippet below shows some of the results of a variety of inputs.
const toSeconds = (() => {
const periods = {h: 3600, m: 60, s: 1};
return time => [.../(\-*\d*\.*\d*)\W*([hms])/g[Symbol.matchAll](time.toLowerCase())]
.reduce((time, [, digits, type]) =>periods[type] * digits + time, 0);
})();
"1h,1m,1s,1,,1s2m3h,3h2m1s,2H2M2S,1h 1H1s1 S1m1M,1.1s,1.2s,s,1h-5m,1 1s,hms"
.split(",")
.forEach(time => log("\"" + time + "\" =" , toSeconds(time)+" seconds"));
function log(...data) {
document.body.appendChild(
Object.assign(
document.createElement("div"), {textContent: data.join(" ")}
)
)
}
BTW in Javascript we put...
- the opening
{
on the same line as the statement,
- use camelCase for naming.
And from many years of C style language experience I would advise you to always delimit statement blocks with {
}
eg Bad if (foo) bar = foo
, Good if (foo) { bar = foo }
+"4h12m32s".replace(/(\d+)h(\d+)m(\d+)s/, (_, h, m, s) => (h * 3600) + (m * 60) + (s * 1))
;) \$\endgroup\$var
andconst
/let
- you could consider replacing them all withconst
(orlet
, when the variable needs to be reassigned) \$\endgroup\$