0

I do not have access to string interpolation in our javascript engine and I need to create a solution for replacing all items in a string with previously defined variables. This needs to also work with nested variables.

I am trying something like:

var vars = {
    obj: [
        { "id": "test" }
    ],
    step1: "obj",
    step2: "0",
    step3: "id"
};
var interp = function(str) {
    var matched = false;
    str = str.replace(/{([^{}]*)}/g, function(match, p1) {
        matched = true;
        return p1.split(/(?=\[)/).reduce((o, p) => o[p[0] == '[' ? p.slice(1, -1) : p], vars) || p1;
    });
    if (matched) interp(str);
    return str;
}
var testStr = 'The string is currently: {{step1}[{step2}][{step3}]}';
console.log( interp(testStr) );
// Output:
// The string is currently: {obj[0][id]}

However if I change testStr to:

var testStr = 'The string is currently: {obj[0][id]}';
// Output:
// The string is currently: test

Why does it seem to skip past the outer-most capture and never return to it?

cw84
  • 85
  • 6
  • If you use typescript, you can set the target to ES5 in tsconfig. Then use template strings (string interpolation) in typescript, and let TS transpile it to ES5 – inorganik Aug 02 '23 at 17:19
  • @inorganik I don't see how that helps. Can TypeScript do this kind of recursive string interpolation? – Barmar Aug 02 '23 at 17:20
  • Variables and properties are different concepts. Seems you mix them up. – trincot Aug 02 '23 at 17:23
  • @Barmar you are right, template literals do no recurse, he would have to refer to the object properties in the string like `vars.step1`, etc – inorganik Aug 02 '23 at 17:26
  • Right, and it means he has to write a parser for `obj[0][id]` to do that. This is not trivial. – Barmar Aug 02 '23 at 17:28
  • 1
    I don't see how ES5 matters here. You can also write your recursive templating engine in ES6 or later and transpile it down. – Bergi Aug 02 '23 at 17:29
  • @Barmar `p1.split(/(?=\[)/).reduce((o, p) => o[p[0] == '[' ? p.slice(1, -1) : p], vars)` – Bergi Aug 02 '23 at 17:37
  • @Bergi `matched` is used to *stop* the recursion -- you keep replacing until there's nothing to replace. However, the `if` condition is not needed -- if the regexp doesn't match, it won't call the function. – Barmar Aug 02 '23 at 17:42
  • @Barmar Oh wait I thought it did `return vars[p1] || match`, but if it returns `… || p1` then it does remove the braces and won't match again – Bergi Aug 02 '23 at 17:50
  • @Bergi Thank you for the snippet. Where exactly does this get placed? It appears that you're parsing the p1 for brackets and if they exist, removing them.. I get a little lost in what is happening after that. – cw84 Aug 02 '23 at 18:37
  • 1
    @cw84 After that it's [accessing the nested value in `vars` based on that property path](https://stackoverflow.com/a/14397052/1048572), without resorting to `eval`. Might've been a bit cleaner to first strip the brackets in `.map()`, and then separately access the properties by name. It goes in place of the `return vars[p1]` - so `return p1.split(…).map(…).reduce((o, p) => o?.[p], vars) ?? p1` – Bergi Aug 02 '23 at 18:58
  • @Bergi I can see what you've done there. This makes sense. The final string is still the same, however. If I change the initial string that we pass in, this works flawlessly. I've updated the original post with your suggestion. Any idea why it does not go back over the string and re-test the outter-most brackets after it finishes the inner-brackets? – cw84 Aug 02 '23 at 19:21
  • 1
    @cw84 It actually *does* recurse. You just forgot to `return` the result from the recursive call and still return the `str` with only the first level replaced. – Bergi Aug 02 '23 at 19:30
  • Fantastic! Thank you. What a small oversight. I think I may need to refactor this slightly because I am finding a situation where I must return the brackets (along with the inner-text) should it not match in the data object. Example: var testStr = '{ "connectionId": "{session_id}", "limit": 10, "order": "desc" }'; I would want to return the session_id if it exists, and leave the brackets around anything that doesn't exist: say the outter-most brackets as this should result in an object-like string. – cw84 Aug 02 '23 at 20:00

0 Answers0