Reputation: 193
I am trying to match all occurrences <% anything %>
inside a template literal.
My regex setup looks like this.
process(template : string , data: Object) {
let re = /<%([^%>]+)?%>/g, match;
while (match = re.exec(template)) {
template = template.replace(match[0], data[match[1]]);
}
return template;
}
if my template looks like this
`<div class="tile">
<span><%ENTRY_NUMBER%></span>
<span><%NAME%> , <%CLASS%> , <%GENDER%> , <%AGE%></span>
</div>`
it will match the first occurrence just fine then keep picking the alternate occurrences. if there is one match inside a html tag it works fine but when i use multiples like in second span element it misses alternate occurrences. I think it has something to do with < and >.
When I separate those occurrences with <>
instead of comma like below it works fine.
<span><%NAME%> <> <%CLASS%> <> <%GENDER%> <> <%AGE%></span>
The result it like this
<span> john <> biology <> male <> 17 </span>
I have examined the regex multiple times but to no avail.
Upvotes: 1
Views: 833
Reputation: 2294
The problem is you don't need a global flag g
becuase you reexecute the expression over and over until will not match but a multiline flag m
let s = `<div class="tile">
<span><%ENTRY_NUMBER%></span>
<span><%NAME%> , <%CLASS%> , <%GENDER%> , <%AGE%></span>
</div>`;
const re = /<%([^\%]+)?%>/,
data = {
ENTRY_NUMBER: '1977',
NAME: 'Blue',
CLASS: 'Vertebrates',
GENDER: 'Male',
AGE: '40'
};
let k = 0;
while (re.test(s) && k++ < 1000) {
match = re.exec(s);
if (match) s = s.replace(match[0], data[match[1]]);
}
console.log(s);
Upvotes: 0
Reputation: 627469
The problem is that re.lastIndex
stores the value that is correct for the previous value of template
, but since you change the template
value the re.lastIndex
is no longer correct for the modified string.
See a simplified example below:
let template = `<%ENTRY_NUMBER%><%NAME%><%CLASS%><%GENDER%><%AGE%>`;
let re = /<%(.+?)%>/g, match;
let data = { 'NAME':'John', 'CLASS':'A3', 'GENDER':'Male', 'AGE':'19' };
while (match = re.exec(template)) {
console.log(re.lastIndex);
template = template.replace(match[0], data[match[1]]);
console.log(template);
}
console.log(template);
// Iteration 1: re.lastIndex = 16
// undefined<%NAME%><%CLASS%><%GENDER%><%AGE%>
// Index is now between <%NAME% and > -> this match will be skipped
// and so on...
Thus, it is better to modify the string on the fly inside a replace callback since all matches will get replaced once they are found, and you won't have to track the indices where to search from.
Use
process(template : string , data: Object) {
return template.replace(/<%(.+?)%>/g, function($0, $1) {
return data[$1] ? data[$1] : $0;
});
}
Note that <%(.+?)%>
matches <%
, then captures any 1 or more chars other than line break chars, as few as possible into Group 1 and then matches %>
. This way, you will also match values that contain %
and >
(it is important if data
contains such keys).
Upvotes: 2