151

I have a string with say: My Name is %NAME% and my age is %AGE%.

%XXX% are placeholders. We need to substitute values there from an object.

Object looks like: {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"}

I need to parse the object and replace the string with corresponding values. So that final output will be:

My Name is Mike and my age is 26.

The whole thing has to be done either using pure javascript or jquery.

4
  • 2
    That looks more like an object than an array Commented Nov 2, 2011 at 1:59
  • 3
    What have you tried so far? Have you looked at the string .replace() method? (Also, you don't have an array there, you have an object.) Commented Nov 2, 2011 at 2:02
  • 2
    That's pretty ugly. Surely you'd be just as well served by {NAME: "Mike", AGE: 26, EVENT: 20}? You'd still require that these keys appear bookended by percent signs in the input string, of course. Commented Nov 2, 2011 at 2:10
  • it seems I am too late to answer, but npm pupa library is quite handy if you want to fill values dynamically and easily. npmjs.com/package/pupa Commented Nov 6, 2023 at 18:54

18 Answers 18

214

The requirements of the original question clearly couldn't benefit from string interpolation, as it seems like it's a runtime processing of arbitrary replacement keys.

However, if you just had to do string interpolation, you can use:

const str = `My name is ${replacements.name} and my age is ${replacements.age}.`

Note the backticks delimiting the string, they are required.


For an answer suiting the particular OP's requirement, you could use String.prototype.replace() for the replacements.

The following code will handle all matches and not touch ones without a replacement (so long as your replacement values are all strings, if not, see below).

var replacements = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"},
    str = 'My Name is %NAME% and my age is %AGE%.';

str = str.replace(/%\w+%/g, function(all) {
   return replacements[all] || all;
});

jsFiddle.

If some of your replacements are not strings, be sure they exists in the object first. If you have a format like the example, i.e. wrapped in percentage signs, you can use the in operator to achieve this.

jsFiddle.

However, if your format doesn't have a special format, i.e. any string, and your replacements object doesn't have a null prototype, use Object.prototype.hasOwnProperty(), unless you can guarantee that none of your potential replaced substrings will clash with property names on the prototype.

jsFiddle.

Otherwise, if your replacement string was 'hasOwnProperty', you would get a resultant messed up string.

jsFiddle.


As a side note, you should be called replacements an Object, not an Array.

Sign up to request clarification or add additional context in comments.

7 Comments

+1. Nice. Though you might want to say return replacements[all] || all to cover %NotInReplacementsList% cases.
This will not work if the replacement value is falseish. So it's better to use this return statement: return all in params ? params[all] : all;
@MichaelHärtl Shouldn't your replacements all be strings? If you want to replace with empty string, better off checking with other means.
@alex I had the situation where replacements could also be integers, and even 0. In this case it didn't work.
@MichaelHärtl Updated to cover that case.
|
34

How about using ES6 template literals?

var a = "cat";
var b = "fat";
console.log(`my ${a} is ${b}`); //notice back-ticked string

More about template literals...

3 Comments

If you have an object with placeholders like the OP, how does string interpolation help that?
Works like a charm! Most pragmatic solution to my placeholding problems, perfect.
This solution doesn't work for runtime replacements
33

Currently there is still no native solution in Javascript for this behavior. Tagged templates are something related, but don't solve it.

Here there is a refactor of alex's solution with an object for replacements.

The solution uses arrow functions and a similar syntax for the placeholders as the native Javascript interpolation in template literals ({} instead of %%). Also there is no need to include delimiters (%) in the names of the replacements.

There are two flavors (three with the update): descriptive, reduced, elegant reduced with groups.

Descriptive solution:

const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';

const replacements = {
  name: 'Mike',
  age: '26',
};

const string = stringWithPlaceholders.replace(
  /{\w+}/g,
  placeholderWithDelimiters => {
    const placeholderWithoutDelimiters = placeholderWithDelimiters.substring(
      1,
      placeholderWithDelimiters.length - 1,
    );
    const stringReplacement = replacements[placeholderWithoutDelimiters] || placeholderWithDelimiters;
    return stringReplacement;
  },
);

console.log(string);

Reduced solution:

const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';

const replacements = {
  name: 'Mike',
  age: '26',
};

const string = stringWithPlaceholders.replace(/{\w+}/g, placeholder =>
  replacements[placeholder.substring(1, placeholder.length - 1)] || placeholder
);

console.log(string);

UPDATE 2020-12-10

Elegant reduced solution with groups, as suggested by @Kade in the comments:

const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';

const replacements = {
  name: 'Mike',
  age: '26',
};

const string = stringWithPlaceholders.replace(
  /{(\w+)}/g, 
  (placeholderWithDelimiters, placeholderWithoutDelimiters) =>
    replacements[placeholderWithoutDelimiters] || placeholderWithDelimiters
);

console.log(string);

UPDATE 2021-01-21

Support empty string as a replacement, as suggested by @Jesper in the comments:

const stringWithPlaceholders = 'My Name is {name} and my age is {age}.';

const replacements = {
  name: 'Mike',
  age: '',
};

const string = stringWithPlaceholders.replace(
  /{(\w+)}/g, 
  (placeholderWithDelimiters, placeholderWithoutDelimiters) =>
  replacements.hasOwnProperty(placeholderWithoutDelimiters) ? 
    replacements[placeholderWithoutDelimiters] : placeholderWithDelimiters
);

console.log(string);

4 Comments

Thanks for this, it's great to have a generic solution for a placeholder string and an object to merge into it like this
@CarlosP, that's the idea, to have a generic solution. But I think it should be something native to the language, as it's a common use case.
This can be simplified a bit more by using regex groups like this: stringWithPlaceholders.replace(/{(\w+)}/g, (fullMatch, group1) => replacements[group1] || fullMatch )
All these solutions suffer from the bug of using "||", since the empty string is also falsy in Javascript. This means that if a replacement string is empty, the placeholder will not be replaced. And it's absolutely valid for a replacement string to be empty. Instead of e.g. replacements[group1] || fullMatch, use replacements.hasOwnProperty(group1) ? replacements[group1] : fullMatch.
16

You can use JQuery(jquery.validate.js) to make it work easily.

$.validator.format("My name is {0}, I'm {1} years old",["Bob","23"]);

Or if you want to use just that feature you can define that function and just use it like

function format(source, params) {
    $.each(params,function (i, n) {
        source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
    })
    return source;
}
alert(format("{0} is a {1}", ["Michael", "Guy"]));

credit to jquery.validate.js team

4 Comments

You definitely wouldn't want to load this plugin just for this, but I'm already using this to validate a form on the page...so thanks for the tip!
Pretty inefficient to create a regex for each number, it would be better to match all numbers and then replace on if the value was found in the array, perhaps?
+ very nice ... and for $.each you could make String.prototype.format=function(p){var s=this,r=function(v,i){s=s.replace(new RegExp("\\{"+i+"\\}","g"),v);};p.forEach(r);return s;} so you don't have to include jquery just for that one ;)
13

As with modern browser, placeholder is supported by new version of Chrome / Firefox, similar as the C style function printf().

Placeholders:

  • %s String.
  • %d,%i Integer number.
  • %f Floating point number.
  • %o Object hyperlink.

e.g.

console.log("generation 0:\t%f, %f, %f", a1a1, a1a2, a2a2);

BTW, to see the output:

  • In Chrome, use shortcut Ctrl + Shift + J or F12 to open developer tool.
  • In Firefox, use shortcut Ctrl + Shift + K or F12 to open developer tool.

@Update - nodejs support

Seems nodejs don't support %f, instead, could use %d in nodejs. With %d number will be printed as floating number, not just integer.

5 Comments

Man, can you pls include a reference link in your answer? 🙏
My bad, I thought it's from an official specification. Thx for the reference!
What if I don't want to just log the string, but keep it as value to do something else with it?
Node.js util.format supports %f.
10

Just use replace()

var values = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};
var substitutedString = "My Name is %NAME% and my age is %AGE%.".replace("%NAME%", $values["%NAME%"]).replace("%AGE%", $values["%AGE%"]);

2 Comments

obligatory "please use MDN as a reference": developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
Thanks for the spanking guys - appreciated ;)
5

You can use a custom replace function like this:

var str = "My Name is %NAME% and my age is %AGE%.";
var replaceData = {"%NAME%":"Mike","%AGE%":"26","%EVENT%":"20"};

function substitute(str, data) {
    var output = str.replace(/%[^%]+%/g, function(match) {
        if (match in data) {
            return(data[match]);
        } else {
            return("");
        }
    });
    return(output);
}

var output = substitute(str, replaceData);

You can see it work here: http://jsfiddle.net/jfriend00/DyCwk/.

2 Comments

Cool, Alex did pretty much exactly the same thing but in fewer lines of code (though ternary operators are likely slower than if..else).
Hey, I gave you a +1! You both did a replace function, yours isn't exactly the same but pretty similar. Your RegExp is different too, the OP would be better off using %% or $$ or similar as delimiters - a single % or % is likely to occur in a string normally, but doubles are unlikely.
5

This allows you to do exactly that

NPM: https://www.npmjs.com/package/stringinject

GitHub: https://github.com/tjcafferkey/stringinject

By doing the following:

var str = stringInject("My username is {username} on {platform}", { username: "tjcafferkey", platform: "GitHub" });

// My username is tjcafferkey on Git

2 Comments

But since you can do it in es6 that is probably a bit overkill?
This lets you store the string in one place in one format and then at a later time replace the corresponding items using only one function. That is to my knowledge not possible to do with es6 template literals. A usage for this would e.g. be translation strings where you will consume the string elsewhere and inject the desired values there.
4

Another solution if you're using node.js is StackExchange's own formatUnicorn utility function (https://www.npmjs.com/package/format-unicorn):

let x = {name:'jason', food:'pine cones'};
let s = '{name} enjoys a delicious bowl of {food}';

let formatted = x.formatUnicorn(s);

Also, a bit of an edge case, but if you aren't using Node but you do just happen to be writing a userscript for SE sites, then formatUnicorn will already be on the String prototype.

1 Comment

Note some incorrect behavior depending on what's being replaced. Seems to be abandonware.
3

If you want to do something closer to console.log like replacing %s placeholders like in

>console.log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>Hello Loreto how are you today is everything allright?

I wrote this

function log() {
  var args = Array.prototype.slice.call(arguments);
  var rep= args.slice(1, args.length);
  var i=0;
  var output = args[0].replace(/%s/g, function(match,idx) {
    var subst=rep.slice(i, ++i);
    return( subst );
  });
   return(output);
}
res=log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright");
document.getElementById("console").innerHTML=res;
<span id="console"/>

you will get

>log("Hello %s how are you %s is everything %s?", "Loreto", "today", "allright")
>"Hello Loreto how are you today is everything allright?"

UPDATE

I have added a simple variant as String.prototype useful when dealing with string transformations, here is it:

String.prototype.log = function() {
    var args = Array.prototype.slice.call(arguments);
    var rep= args.slice(0, args.length);
    var i=0;
    var output = this.replace(/%s|%d|%f|%@/g, function(match,idx) {
      var subst=rep.slice(i, ++i);
      return( subst );
    });
    return output;
   }

In that case you will do

"Hello %s how are you %s is everything %s?".log("Loreto", "today", "allright")
"Hello Loreto how are you today is everything allright?"

Try this version here

1 Comment

i made a variation on your function without prototypes formatMessage(message: string, values: string[]) { let i = 0; return message.replace(/%\w+%/g, (match, idx) => { return values[i++]; }); } this takes message to format and array of replace values and looks for %SOME_VALUE%
3

I have written a code that lets you format string easily.

Use this function.

function format() {
    if (arguments.length === 0) {
        throw "No arguments";
    }
    const string = arguments[0];
    const lst = string.split("{}");
    if (lst.length !== arguments.length) {
        throw "Placeholder format mismatched";
    }
    let string2 = "";
    let off = 1;
    for (let i = 0; i < lst.length; i++) {
        if (off < arguments.length) {
            string2 += lst[i] + arguments[off++]
        } else {
            string2 += lst[i]
        }
    }
    return string2;
}

Example

format('My Name is {} and my age is {}', 'Mike', 26);

Output

My Name is Mike and my age is 26

Comments

3

ES6:

const strFormat = (str, ...args) => args.reduce((s, v) => s.replace('%s', v), str);

// Use it like:
const result = strFormat('%s is %s yrs old', 'name', 23);

1 Comment

While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. You can find more information on how to write good answers in the help center: stackoverflow.com/help/how-to-answer . Good luck 🙂
2

Lots of good/similar answers here. I wanted the ability to easily get a nested key in an object (or perhaps some JSON data structure) for substitution, so I took the following simple approach:

const getKey = (d, path) => {
  // path can be a string like 'key1.key2' or an iterable of keys
  if (typeof(path) === 'string') {
    path = path.split('.')
  }
  return path.reduce((x, y) => x[y], d)
}

const inject = (str, obj) => str.replace(/\${(.*?)}/g, (x,g)=> getKey(obj, g));


// Example

> const str = 'there are ${a} ways to ${b.c}'
undefined
> inject(str, {'a':'many', 'b': {'c': 'skin a cat'}})
'there are many ways to skin a cat'

Some inspiration from this and this.

Comments

1

As a quick example:

var name = 'jack';
var age = 40;
console.log('%s is %d yrs old',name,age);

The output is:

jack is 40 yrs old

1 Comment

That's great, unless you want to do something besides log it to the console.
1

Here is another way of doing this by using es6 template literals dynamically at runtime.

const str = 'My name is ${name} and my age is ${age}.'
const obj = {name:'Simon', age:'33'}


const result = new Function('const {' + Object.keys(obj).join(',') + '} = this.obj;return `' + str + '`').call({obj})

document.body.innerHTML = result

1 Comment

Holy injection attack vulnerability, Batman! Requires sanitizing str for backticks.
1
const stringInject = (str = '', obj = {}) => {
  let newStr = str;
  Object.keys(obj).forEach((key) => {
    let placeHolder = `#${key}#`;
    if(newStr.includes(placeHolder)) {
      newStr = newStr.replace(placeHolder, obj[key] || " ");
    }
  });
  return newStr;
}
Input: stringInject("Hi #name#, How are you?", {name: "Ram"});
Output: "Hi Ram, How are you?"

Comments

0

This is a merged solution of Gerson Diniz and Shubham Vyas.

It is possible to pass a set of arguments or an object.

function strSwap(str) {

  if (!str) return null;
  
  let args = [];
  for (let a of arguments) 
    args.push(a);
  args.shift();
  
  if (!args.length) return null;
  
  // replacement by object - {{prop}}
  if (!!(args[0].constructor && args[0].constructor.name.toLowerCase() === 'object')) {
    for (let i in args[0]) {
      let n = `{{${i}}}`;
      str = str.includes(n) ? str.replaceAll(n, args[0][i] + '') : str;
    }
  }
  // replacement by placeholders - %s
  else {
    str = args.reduce((s, v) => s.replace('%s', v), str);
  }
  
  return str;

}

// ---------------------

console.log(strSwap('Hello %s, my name is %s.', 'alice', 'bob'));

console.log(strSwap('Hello {{a}}, my name is {{b}}. Hello {{b}}.', {
  a: 'alice', 
  b: 'bob'
}));

Comments

0

Alex's code rock! Thanks!

Small modification to replacement function to be able to use the replacement variables by name directly.

var replacements = {
    "NAME": "Mike",
    "AGE": "26",
    "EVENT": "20"
},    
str = 'My Name is ${NAME} and my age is ${AGE}, the following ${placeholder} is missing.';

str = str.replace(/\$\{(.*?)\}/g, function(placeholder, name) {
    return replacements[name] || placeholder;
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.