1

I am trying to extract some data from user input that should follow this format: 1d 5h 30m, which means the user is entering an amount of time of 1 day, 5 hours and 30 minutes.

I am trying to extract the value of each part of the input. However, each group is optional, meaning that 2h 20m is a valid input.

I am trying to be flexible in the input (in the sense that not all parts need to be input) but at the same time I don't watch my regex to match some random imput like asdfasdf20m. This one should be rejected (no match).

So first I am getting rid of any separator the user might have used (their input can look like 4h, 10m and that's ok):

input = input.replace(/[\s.,;_|#-]+/g, '');

Then I am capturing each part, which I indicate as optional using ?:

var match = /^((\d+)d)?((\d+)h)?((\d+)m)?$/.exec(input);

It is kind of messy capturing an entire group including the letter when I only want the actual value, but I cannot say that cluster is optional without wrapping it with parentheses right?

Then, when an empty group is captured its value in match is undefined. Is there any function to default undefined values to a particular value? For example, 0 would be handy here.

An example where input is "4d, 20h, 55m", and the match result is:

["4d20h55m", "4d", "4", "20h", "20", "55m", "55", index: 0, input: "4d20h55m"]

My main issues are:

  • How can I indicate a group as optional but avoid capturing it?

  • How can I deal with input that can potentially match, like abcdefg6d8m?

  • How can I deal with an altered order? For example, the user could input 20m 10h.

When I'm asking "how to deal with x" I mean I'd like to be able to reject those matches.

dabadaba
  • 9,064
  • 21
  • 85
  • 155
  • You might want to take a look at non capturing groups [`(?:pattern)`](http://stackoverflow.com/questions/3512471/what-is-a-non-capturing-group-what-does-a-question-mark-followed-by-a-colon). – Sebastian Proske Feb 22 '17 at 17:19
  • As for "How can I deal with an altered order?" how do you *want* to deal with it? Is it `20m 10h` valid or should it be rejected? How about `4d 5d 6d`? Would it be rejected outright? How about `4D 5H 6M` or `100000m 1s`? – Jordan Running Feb 22 '17 at 17:27
  • And what delimiters are allowed? `1d, 1h` is allowed, but is `1d:1h` allowed? How about `1d, 1h,` (trailing delimiter), `1d%1h` or `1d1h`? – Jordan Running Feb 22 '17 at 17:34
  • Is an empty string acceptable? – Laurel Feb 22 '17 at 17:36
  • @Jordan yes, as I think I mentioned it's just an amount of time so `1d 1h` is the same than `1h 1d`. I just threw some common delimiters users might think of using. Only minutes, hours and days are allowed, so `1s` is not allowed, and `1000000m` is allowed too. As of uppercase, sure, I should have added that possibility too. – dabadaba Feb 22 '17 at 19:24
  • @Laurel no, it is not acceptable – dabadaba Feb 22 '17 at 19:24
  • What if the same unit is used multiple times, e.g. `4d 5d 6d`? – Jordan Running Feb 22 '17 at 19:33
  • @Jordan not accepted then – dabadaba Feb 23 '17 at 08:09

1 Answers1

0

As variant:

HTML:

<input type="text">
<button>Check</button>
<div id="res"></div>

JS:

var r = [];
document.querySelector('button').addEventListener('click', function(){
  var v = document.querySelector('input').value;
  v.replace(/(\d+d)|(\d+h)|(\d+m)/ig, replacer);
  document.querySelector('#res').innerText = r;
}, false);

function trim(s, mask) {
    while (~mask.indexOf(s[0])) {
        s = s.slice(1);
    }
    while (~mask.indexOf(s[s.length - 1])) {
        s = s.slice(0, -1);
    }
    return s;
}

function replacer(str){
  if(/d$/gi.test(str)){
    r[0] = str;
  }
  else if(/h$/gi.test(str)){
    r[1] = str;
  }
  else if(/m$/gi.test(str)){
    r[2] = str;
  }
  return trim(r.join(', '), ',');
}

See here.

Choo Hwan
  • 416
  • 3
  • 12