Why JS Regexp.exec returns an array with more elements than expected?

I’m attempting to regex match various duration strings (e.g. 1d10h, 30m, 90s, etc.) and have come up with a regex string to split the string into pieces, but it seems that I’m getting two undefined results at the ends that shouldn’t be there. I imagine it has to do with the greedy matching via the ? groupings, but I’m not sure how to fix it.

My code looks like this:

const regex = /^(d+?[d])?(d+?[h])?(d+[m])?(d+[s])?$/gmi
const results = regex.exec('1d10h')

and the results I get look like so:

[
  "1d10h",
  "1d",
  "10h",
  undefined,
  undefined,
]

I was only expecting the first three results (and in fact, I only really want 1d and 10h) but the two remaining undefined results keep popping up.

Answer

You have 4 groups in the regular expression – each enclosed with braces () and enumerated naturally – the earlier opening brace appear in the expression the lower order index a group has.

And, of course, the whole match that could be named a “zero” group.

So, result of regex.exec('1d10h') contains 5 items:

  • results[0] – the whole expression match
  • results[i] – match of each group, i in {1,2,3,4}

Since in this case each group is optional (followed by ?) – it is allowed to have undefined in place of any unmatched group.

It is easy to see that if you remove a ? symbol after an unmatched group, the whole expression will fail to match and hence regex.exec('1d10h') will return null.

To get rid of undefined elements just filter them out:

const result = regex.exec('1d10h').filter(x => x);