chris
chris

Reputation: 4351

Regex replacement is not being performed on all matches

Can anyone see why the following code does not perform the regex operations on all the {{ ... }} placeholders? The input string below is just a stripped down version of the original.

https://jsfiddle.net/2L12jr3u/2/

var input = "{{ %1$@ }} / {{ %1$@ }} ({{ %2$@ }}) {{ %1$@ }} {{ %1$@ }} {{ %1$d }} {{ %1$@ }} of {{ %2$d }} of {{ %3$d }}";

var regex = /(\{\{ \%(\d)\$(.) \}\})/g;

var match;
while (match = regex.exec(input)) {
    console.log(match);
    input = input.replace(match[0], '%@' + match[2]);
}

console.log(input);

Upvotes: 2

Views: 50

Answers (3)

Hawkings
Hawkings

Reputation: 533

First I suggest to remove unnecessary groups in the regular expression,

/\{\{ %(\d)\$. \}\}/g instead of /(\{\{ %(\d)\$(.) \}\})/g. Then, to replace you can use a much shorter approach (and also more clear in my oppinion):

var input = "{{ %1$@ }} / {{ %1$@ }} ({{ %2$@ }}) {{ %1$@ }} {{ %1$@ }} {{ %1$d }} {{ %1$@ }} of {{ %2$d }} of {{ %3$d }}";
var output = input.replace(/\{\{ %(\d)\$. \}\}/g, "%@$1");

The final value of output is %@1 / %@1 (%@2) %@1 %@1 %@1 %@1 of %@2 of %@3

Upvotes: 0

dfsq
dfsq

Reputation: 193301

Since you are using exec to match multiple occurrences, in each iteration it starts searching from the last matched index:

If your regular expression uses the "g" flag, you can use the exec() method multiple times to find successive matches in the same string. When you do so, the search starts at the substring of str specified by the regular expression's lastIndex property (test() will also advance the lastIndex property).

In your case it's much simpler and cleaner to use String.prototype.replace method:

var input = "{{ %1$@ }} / {{ %1$@ }} ({{ %2$@ }}) {{ %1$@ }} {{ %1$@ }} {{ %1$d }} {{ %1$@ }} of {{ %2$d }} of {{ %3$d }}";

var regex = /(\{\{ %(\d)\$(.) \}\})/g;

input = input.replace(regex, '%@$2');

alert(input);

Upvotes: 2

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 627607

It is because you change the input variable while exec has not finished. The index is moving forward, but the string gets shorter.

Add another variable, or wrap in a separate function, or use replace as suggested by @dfsq:

var input = "{{ %1$@ }} / {{ %1$@ }} ({{ %2$@ }}) {{ %1$@ }} {{ %1$@ }} {{ %1$d }} {{ %1$@ }} of {{ %2$d }} of {{ %3$d }}";

var regex = /(\{\{ \%(\d)\$(.) \}\})/g;
var output = input;
var match;

while (match = regex.exec(input)) {
    console.log(match);
    output = output.replace(match[1], '%@' + match[2]);
}

alert(output);

Upvotes: 3

Related Questions