9

I would like to take text that I generated and stored in a string and use it like a template literal.

var generatedText = "Pretend this text was generated and then stored in a variable. "; 
generatedText = "But I still need to use it as a template it to get ${variable}.";

var variable = "Successs!!!!";

console.log(generatedText);
//prints 'But I still need to interpolate it to get ${variable}.'
//how can I make it print using variable in it like a template as if it were doing this
console.log(`But I still need to use it as a template it to get ${variable}.`);
//prints 'But I still need to use it as a template it to get Successs!!!!.'

How can I get generated text to become a template string?

generatedText must start in a variable so I need to find a way to convert it to a template string if possible.

Edit:

I didn't think I would have to put this but also I don't want to use eval to risk evaluating random code...

Trevor
  • 139
  • 1
  • 7
  • 3
    Why not directly use template literal instead of first saving as string and than trying as template literal ? what you're trying to achieve ? – Code Maniac Aug 20 '19 at 02:01
  • It's not clear what you're asking. – mwilson Aug 20 '19 at 02:02
  • 1
    @MeirKeller comments aren't meant for answers. If you think you have a good answer, post it as an answer. – mwilson Aug 20 '19 at 02:08
  • 1
    https://stackoverflow.com/questions/29182244/convert-a-string-to-a-template-string – epascarello Aug 20 '19 at 02:10
  • See also "Can ES6 template literals be substituted at runtime" https://stackoverflow.com/questions/29182244/convert-a-string-to-a-template-string – Chris Aug 20 '19 at 02:32
  • 1
    Reopening because none of the top several answers of the duplicate are as good as CertainPerformance’s. There are some near the bottom that are close, but it also seems that nested properties aren’t required here. – Ry- Aug 20 '19 at 04:50

4 Answers4

19

For the general situation, you can use a replacer function to replace every occurrence of ${someProp} with the someProp property on an object:

const interpolate = (str, obj) => str.replace(
  /\${([^}]+)}/g,
  (_, prop) => obj[prop]
);

const generatedText = "But I still need to use it as a template it to get ${variable}.";
const variable = "Successs!!!!";

console.log(interpolate(generatedText, { variable }));

The regular expression \${([^}]+)} means:

  • \$ - Literal $
  • { - Literal {
  • ([^}]+) First (and only) capture group:
    • [^}]+ - One or more characters which are not }
  • } - Literal }

Since prop is the property name found in between the brackets, replace with obj[prop] to replace with the desired replacement.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
7

The interpolate function below is an extended version of this answer that adds support for simple nested object field references (e.g.: a.b.c)

function interpolate(s, obj) {
  return s.replace(/[$]{([^}]+)}/g, function(_, path) {
    const properties = path.split('.');
    return properties.reduce((prev, curr) => prev && prev[curr], obj);
  })
}

console.log(interpolate('hello ${a.b.c}', {a: {b: {c: 'world'}}}));
// Displays 'hello world'
Rob van der Leek
  • 1,486
  • 15
  • 13
2

The interpolate function below is an extended version of the above solution adds support for simple nested object field references, with addition of arrays (e.g.: a[0][2].b.c)

const interpolate = (str, obj) => {
  return str.replace(/\${([^}]+)}/g, (_, target) => {
    let keys = target.split(".");
    return keys.reduce((prev, curr) => {
      if (curr.search(/\[/g) > -1) {
        //if element/key in target array is array, get the value and return
        let m_curr = curr.replace(/\]/g, "");
        let arr = m_curr.split("[");
        return arr.reduce((pr, cu) => {
          return pr && pr[cu];
        }, prev);
      } else {
        //else it is a object, get the value and return
        return prev && prev[curr];
      }
    }, obj);
  });
};

let template = "hello ${a[0][0].b.c}";
let data = {
  a: [
    [{
      b: {
        c: "world",
        f: "greetings"
      }
    }, 2], 3
  ],
  d: 12,
  e: 14
}
console.log(interpolate(template, { ...data
}));
1

You should emulate a template literal instead, because letting text from ~somewhere~ run arbitrary JavaScript like a real template literal’s ${} sections can usually isn’t a good idea:

generatedText.replace(/\$\{variable}/g, variable);
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • Certain performances answer met my needs a little closer because I could replace multiple instances of different variables in a single pass, but this answer is also correct, does what I was asking and doesn't use eval. – Trevor Aug 20 '19 at 04:41