1

I need to regex string with a year inside. Template is 'Year-<4 digits>-"high OR low"-level'.

I've built this regex: /Year-\d{4}-\b(low|high)\b-level/gi; In online regex testers my strings pass the check. Sample code:

const template = /Year-\d{4}-\b(low|high)\b-level/gi;

const txtArr = ['Year-2019-low-level', 'Year-2019-high-level', 'Year-low-level', 'Year-high-level', 'Year-2018-low-level', 'Year-2018-low-level']

for (const s of txtArr) {
    console.log(template.test(s), s);
}

I expect 2 of sample strings to not pass, but 4 should pass. But they dont - only 2 of them pass. Also in browser console they don't pass. Tried in FF and Chrome. Can't understand why. Also, if I copy the string that is not passing the match and just make

console.log(template.test('Year-2018-low-level'), 'Year-2018-low-level');

it passes! I've got only one idea: looks like in every iteration of loop something is not reset in regex, and it keeps something in memory, that is not letting match pass.

P.S. I even copied the same string which must pass the test to array, like that:

const txtArr = ['Year-2019-low-level', 'Year-2019-low-level', 'Year-2019-low-level', 'Year-2019-low-level', 'Year-2019-low-level', 'Year-2019-low-level']

and the results are true-false-true-false-true... Why? And how to fix?

Naryck
  • 59
  • 1
  • 9

3 Answers3

1

An alternative is to use !!s.match(template) instead of template.test(s), so you don't need to modify your regex.

Working example: https://codesandbox.io/s/zen-carson-z9cq6

An explanation to the weird behavior:

The RegExp object keeps track of the lastIndex where a match occurred, so on subsequent matches it will start from the last used index, instead of 0.

from this StackOverflow question: Why does a RegExp with global flag give wrong results?

Michele
  • 751
  • 1
  • 8
  • 16
1

I found an explanation here: https://siderite.dev/blog/careful-when-reusing-javascript-regexp.html

"The moral of the story is to be careful of constructs like _reg.test(input); when _reg is a global regular expression. It will attempt to match from the index of the last match in any previous string."

So the problem comes from the way the global statement is treated.

The author of the blog also describes the very same problem you have:

"Here is a case that was totally weird. Imagine a javascript function that returns an array of strings based on a regular expression match inside a for loop. In FireFox it would return half the number of items that it should have."

What you could do to avoid this problem is either not using the global keyword, or instanciate a new regex at each iteration:

const txtArr = ['Year-2019-low-level', 'Year-2019-high-level', 'Year-low-level', 'Year-high-level', 'Year-2018-low-level', 'Year-2018-low-level']

for (const s of txtArr) {
    console.log(/Year-\d{4}-\b(low|high)\b-level/gi.test(s), s);
}
Siderite Zackwehdex
  • 6,293
  • 3
  • 30
  • 46
olinox14
  • 6,177
  • 2
  • 22
  • 39
0

I changed your regex and its working, with this one:

const template = /Year-\d{4}-(low|high)-level/

Nicolas
  • 328
  • 2
  • 10