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);
}
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).