Search code examples
javascriptobjectpropertiesevaluationtemplate-literals

Function to replace a substring with a function value in javascript


I need a function which will accept test arguments and return a formatted response

format('Hello, {name}!', {name: 'world'}); // Hello, world!

format('Hello, {user.name.first} {user.name.last}!', {user: {name: {first: 'John', last: 'Smith'}}}); // Hello, John Smith!

format('Hello, {user.name.first.f1} {user.name.last}!', {user: {name: {first: {f1:'John'}, last: 'Smith'}}}); // Hello, John Smith!

I did this. But is it a good approach ? Basic idea was to convert the string to template. Is there any better approach ?

var format = (str, obj) => {

  str = str.replaceAll('{', '${obj.');
  let newStr = eval('`' + str + '`');
    
  console.log(newStr);
}

Solution

  • The replaceAll method accepts a function as a second parameter, the parameter is the matched substring. You can use regex to match {abc.def.ghi} and with the function return the value at this path.

    The accessByPath function is based on this SO question.

    const accessByPath = (obj, path) => {
        const parts = path.split('.')
        // TODO: Properly handle invalid path
        for (let i=0; i < parts.length; i++){
            obj = obj[parts[i]];
        };
        return obj;
    };
    
    const format = (str, obj) => {
      return str.replaceAll(/{[^}]+}/g, substring => accessByPath(obj, substring.slice(1, -1)))
    }
    
    console.log(format('Hello, {name}!', {name: 'world'}))
    console.log(format('Hello, {user.name.first} {user.name.last}!', {user: {name: {first: 'John', last: 'Smith'}}}))
    console.log(format('Hello, {user.name.first.f1} {user.name.last}!', {user: {name: {first: {f1:'John'}, last: 'Smith'}}}))

    As you asked whether yours is a good approach: Using eval in many cases introduces security risks (e.g. in your case if a user somehow can control the str value, they can execute any javascript on your page).