Mowzer Mowzer - 10 days ago 5
Javascript Question

Weird behavior with Regex matching

Here is a Codepen version.

My goal is to iterate over the elements of an array, search for a regex matching

~@
something
@~
, remove those "bookends" identifying the match, then join the array. In the following example, the result should look like
a/b/c
.

However, in actuality, I get the result
a/~@b@~/c
. To make things even more confusing, when I reverse the order of the array elements, the problem result changes from the
~@b@~
to the
~@c@~
. And, finally, to make things downright weird, the problem seems to resolve itself by adding a simple
match
method which, itself, always returns a value of
false
. See comments for those in code. And see for yourself.

What is causing this strange behavior? And what is the correct way to iterate over these elements and do the substitution I describe?



function myFunction() {
var a = ["a", "~@b@~", "~@c@~"]
var re = /~@(.*?)@~/g;
var i = a.length;
// Uncomment below line to see the problem change from ~@b@~ to ~@c@~
//a.reverse();
while (i--) {
console.log('i', i, 'str', a[i]);
var match = re.exec(a[i]);
console.log('match', match);
// Uncomment the below line to see it work properly.
//console.log('test', re.test(a[i]));
if (match && match[0] && match[1]) {
a[i] = a[i].replace(match[0], match[1]);
console.log('a[i]', a[i]);
}
}
var res = a.join('/');
document.getElementById("demo").innerHTML = res;
}

<p>
My goal is to print the string: <code>a/b/c</code>. See weird <i>uncomment</i> fix in JS.
<button onclick="myFunction()">Click Here</button>
</p>
<p id="demo"></p>




Answer

The reason for the behavior is that your regex has the global flag (g) but you're only executing it once; that means its lastIndex is set and the next time you run it, it tries to start from where it left off.

Either remove the g flag, or add

re.lastIndex = -1;

...as the first line in the while loop:

function myFunction() {
  var a = ["a", "~@b@~", "~@c@~"]
  var re = /~@(.*?)@~/;             // <=== Note `g` removed
  var i = a.length;
  var t;
  while (i--) {
    var match = re.exec(a[i]);
    if (match && match[0] && match[1]) {
      a[i] = a[i].replace(match[0], match[1]);
    }
  }
  var res = a.join('/');
  document.getElementById("demo").innerHTML = res;
}
<p>
  My goal is to print the string: <code>a/b/c</code>. See weird <i>uncomment</i> fix in JS.
  <button onclick="myFunction()">Click Here</button>
</p>
<p id="demo"></p>

However, if you leave that g flag on the regex, you can replace the entire contents of the while loop with

a[i] = a[i].replace(re, "$1");

function myFunction() {
  var a = ["a", "~@b@~", "~@c@~"]
  var re = /~@(.*?)@~/;
  var i = a.length;
  var t;
  //  Uncomment below line to see the problem change from ~@b@~ to ~@c@~
  //a.reverse();
  while (i--) {
    a[i] = a[i].replace(re, "$1");
  }
  var res = a.join('/');
  document.getElementById("demo").innerHTML = res;
}
<p>
  My goal is to print the string: <code>a/b/c</code>. See weird <i>uncomment</i> fix in JS.
  <button onclick="myFunction()">Click Here</button>
</p>
<p id="demo"></p>

...which also has the advantage of handling entries in the form "~@b@~~@c@~" (because it replaces all occurrences in the string, not just the first one).