Skip to main content
Commonmark migration
Source Link

##Named Capture Groups

Named Capture Groups

##A more robust solution

A more robust solution

##Named Capture Groups

##A more robust solution

Named Capture Groups

A more robust solution

added 447 characters in body
Source Link
Blindman67
  • 22.3k
  • 2
  • 16
  • 40

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.

The snippet below shows some of the results of a variety of inputs.

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.

Source Link
Blindman67
  • 22.3k
  • 2
  • 16
  • 40

##Named Capture Groups

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

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 }