2

I'm currently trying to parse some user input into some regex groups so I can put the following inputs into the outputs.

The current regex I have is

^[0-9]+[d,D,h,H,m,M]$

This successfully matches a single date, for example 1d or 2h. However, I need it to dynamically work for every single one, even if some are not provided by the user.

input: "5d"
output: {days: 5, hours: 0, minutes: 0}

input: "1d 2h"
output: {days: 1, hours: 2, minutes: 0}

input: "1d 5h 2m"
output: {days: 1, hours: 5, minutes: 2}

input: "2m"
output: {days: 0, hours: 0, minutes: 2}

The output doesn't necessarily have to be in a js object, simply using regex groups would also be beneficial and something I could work with.

Any help would be very appreciated as I'm not sure what else to do.

1
  • Why not do '1d 5h 2m'.match(/[0-9]+[d,D,h,H,m,M]/gm) which returns ['1d', '5h', '2m'] Commented Aug 23, 2022 at 0:31

4 Answers 4

3

You could expand your regex to include three named capture groups for the days, hours and minutes, then use the group values to populate an output object:

const parseInterval = s => { 
  let res = regex.exec(s) || {}
  return { 
    days : +res.groups.days || 0,
    hours : +res.groups.hours || 0,
    minutes : +res.groups.minutes || 0
  }
}

const input = [ "5d", "1D 2h", "1d 5H 2m", "2M"]
const regex = /(?:(?<days>\d+)d)?\s*(?:(?<hours>\d+)h)?\s*(?:(?<minutes>\d+)m)?/i

const result = input.map(parseInterval)

console.log(result)

Sign up to request clarification or add additional context in comments.

3 Comments

This is unnecessary complex regex, with no benefit of using "groups names", which makes it almost twice slower than necessary...
@Xyu I guess I disagree on that; the benefit of group names is that the code becomes (IMO) very readable and easy to understand. As for 2x slower, how did you measure that?
That is why I chose this solution as the answer. I'm unsure if it is true about how slow it is. But for what I need it doesn't matter. I like the way in which you did this. Super easy to understamd. Although the other options are good too. thanks :)
3

You can use RegExp.exec() to get all global matches with a single regex:

function parseDate(input)
{
  const reg = /([0-9]+)([dhm])(?=[0-9\s]|$)/gi,
        result = {days: 0, hours: 0, minutes: 0},
        map = {d: "days", h: "hours", m: "minutes"};

  let match;
  while((match = reg.exec(input.value)))
    result[map[match[2]]] += +match[1]; //this will sum multiple matched values ie "1d 2d 3d" will become 6 days remove "+" from "+=" if this behaviour is not desired

  console.clear();
  console.log(result);
}

parseDate(dateInput);
<input id="dateInput" oninput="parseDate(this)" value="3d 2h 1m">

Comments

2

Here is a way if you can live with 3 regex to fill the object:

let input = [
  '5d',       // {days: 5, hours: 0, minutes: 0}
  '1d 2h',    // {days: 1, hours: 2, minutes: 0}
  '1d 5h 2m', // {days: 1, hours: 5, minutes: 2}
  '2m'        // {days: 0, hours: 0, minutes: 2}
].forEach(str => {
  let d = str.match(/\b([0-9]+)d\b/);
  let h = str.match(/\b([0-9]+)h\b/);
  let m = str.match(/\b([0-9]+)m\b/);
  let obj = {
    days:    d ? Number(d[1]) : 0,
    hours:   h ? Number(h[1]) : 0,
    minutes: m ? Number(m[1]) : 0,
  }
  console.log(str + ' => ' + JSON.stringify(obj));
});

Result:

5d => {"days":5,"hours":0,"minutes":0}
1d 2h => {"days":1,"hours":2,"minutes":0}
1d 5h 2m => {"days":1,"hours":5,"minutes":2}
2m => {"days":0,"hours":0,"minutes":2}

2 Comments

it only gets days
@vanowm: You are right, I updated the snippet.
1

you can use a function like this

const myfun = (s) => {
    const days = s.match(/[0-9]+[d,D]/) ? s.match(/[0-9]+[d,D]/)[0] : "0d" 
    const hours = s.match(/[0-9]+[h,H]/) ? s.match(/[0-9]+[h,H]/)[0] : "0d" 
    const minutes = s.match(/[0-9]+[m,M]/) ? s.match(/[0-9]+[m,M]/)[0] : "0d" 

return  {
        days: days.slice(0, -1),
        hours: hours.slice(0, -1),
        minutes:minutes.slice(0, -1)
    }
}

console.log(myfun("5d")) // {days: 5, hours: 0, minutes: 0}
console.log(myfun("1d 2h")) // { days: '1', hours: '2', minutes: '0' }
console.log(myfun("1d 5h 2m")) // { days: '1', hours: '5', minutes: '2' }
console.log(myfun("2m")) // { days: '0', hours: '0', minutes: '2' }
console.log(myfun("randomString")) // { days: '0', hours: '0', minutes: '0' }

1 Comment

It's not a good practice execute the same regex multiple times, it's better store result in a variable instead. Also, slice(0, -1) is redundant, it does nothing. Finally, input ,2 will return { "days": "2", "hours": "2", "minutes": "2" }

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.