Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a regex captured group as an object key within a String replace

Can anyone help explain the behavior shown in the code snipit below (from a NodeJS 12 console)? I am trying to replace part of a string with the result from a capture group. This actually works, but when I use that capture group result as the key in an object, instead of the first capture group it's literally using $1.

> tmp 
{ LOCALAS: 'world' } 
> a 
'let the {{{LOCALAS}}} go around' 

> a.replace(/\{\{\{(.+)\}\}\}/g, "$1"); 
'let the LOCALAS go around' 
> a.replace(/\{\{\{(.+)\}\}\}/g, tmp["LOCALAS"]); 
'let the world go around' 
> a.replace(/\{\{\{(.+)\}\}\}/g, tmp["$1"]); 
'let the undefined go around'

> tmp["$1"] = "NO"; 
'NO' 
> a.replace(/\{\{\{(.+)\}\}\}/g, tmp["$1"]); 
'let the NO go around'

... a friend has provided me an answer that works, but I'd still love to know why the above acts the way it does.

> a.replace(/\{\{\{(.+)\}\}\}/g, function(_,k){return [tmp[k]]});
'let the world go around'
like image 324
thechane Avatar asked Feb 25 '26 16:02

thechane


1 Answers

I've been looking for the same thing, and came across this question.

In my case I needed to use the captured value in an eval, not a lookup.

As pointed out by others, we can only use the capture group in plain text substitution.

So this works:

str.replace(/\{\{\{(.+?)\}\}\}/g, '$1')

but this does not work:

str.replace(/\{\{\{(.+?)\}\}\}/g, tmp['$1'])

This is because tmp['$1'] will be evaluated before replace is called.


Since you know the available keys, you can simply iterate over them and do the replacement like so:

let str = 'this is a {{{adj}}} text used as a {{{adj}}} {{{noun}}}';
const tmp = {
    'adj'  : 'short', 
    'noun' : 'example',
};

Object.keys(tmp).forEach(key => {
    const replaceRegex = new RegExp(`\{\{\{${key}\}\}\}`, 'g');
    str = str.replace( replaceRegex, tmp[key] );
});

console.log(str); // prints: "this is a short text used as a short example"

In my case, the values were unknown, so I solved it by extracting the values beforehand and then iterating over them:

let str = 'Mario has {{{"Mario".length}}} letters in his name, and Bowser has {{{"Bowser".length}}}';

const expressions = [];
const captureRegex = /\{\{\{(.*?)\}\}\}/g;
const matches = str.matchAll(captureRegex );
for (const match of matches) expressions.push(match[1]);

expressions.forEach(expression => {
    const replaceRegex = new RegExp(`\{\{\{${expression}\}\}\}`, 'g');
    str = str.replace( replaceRegex, eval(expression) );
});

console.log(str); // prints: "Mario has 5 letters in his name, and Bowser has 6"
like image 172
Levi Avatar answered Feb 27 '26 06:02

Levi