Replacing Text Inside of Curley Braces JavaScript
Asked Answered
H

5

20

I am trying to use JavaScript to dynamically replace content inside of curly braces. Here is an example of my code:

var myString = "This is {name}'s {adjective} {type} in JavaScript! Yes, a {type}!";
var replaceArray = ['name', 'adjective', 'type'];
var replaceWith = ['John', 'simple', 'string'];

for(var i = 0; i <= replaceArray.length - 1; i ++) {
  myString.replace(/\{replaceArray[i]\}/gi, replaceWith[i]);
}

alert(myString);

The above code, should, output "This is John's simple string in JavaScript! Yes, a string!".

Here is what happens:

  1. we are given a string with values in braces that need replaced
  2. a loop uses "replaceArray" to find all of the values in curly braces that will need replaced
  3. these values, along with the curly braces, will be replaced with the corresponding values in the "replaceWith" array

However, I am not having any luck, especially since one value may be replaced in multiple locations, and that I am dealing a dynamic value inside of the regular expression.

Can anyone help me fix this, using a similar setup as above?

Hamforrd answered 17/3, 2011 at 2:53 Comment(1)
I'm signed in as a different user this time... @William: I have not, let me try that! @rsp: I am trying to use pure JavaScript on this one, because there is lots of other jQuery surrounding this block of code, and I am hope to speed up performance by using just JavaScript.Hamforrd
I
20

First, String.replace is not destructive - it doesn't change the string itself, so you'll have to set myString = myString.replace(...). Second, you can create RegExp objects dynamically with new RegExp, so the result of all that would be:

var myString = "This is {name}'s {adjective} {type} in JavaScript! Yes, a {type}!",
    replaceArray = ['name', 'adjective', 'type'],
    replaceWith = ['John', 'simple', 'string'];

for(var i = 0; i < replaceArray.length; i++) {
    myString = myString.replace(new RegExp('{' + replaceArray[i] + '}', 'gi'), replaceWith[i]);
}
Incommunicative answered 17/3, 2011 at 3:0 Comment(4)
I prefer this method: myString = myString.split('{' + replaceArray[i] + '}').join(replaceWith[i]); (for readability and performance reasons).Subirrigate
Is it faster to use a new RegEx obj or to directly put the regex inside of the replace statement like: myString.replace('/[{].*[}]/', text)?Lucindalucine
@Lucindalucine First, your example code there wouldn't work, since the function would be looking for the literal string /[{].*[}]/. The performance difference between the two should be sufficiently small for almost all purpose that it is negligible, but unless you need to create RegExp dynamically (such as in the case in the answer), creating the RegExp directly is much more readable, since creating a RegExp object requires a string to be passed you need to double escape any backslashes. If in doubt, benchmark the two code and find out for yourself which is fasterIncommunicative
@YiJiang It would work just fine without the single quotes around the regex. var myString = 'foo/{bar}'; var myNewString = myString.replace(/[{].*[}]/, 'hi'); //"foo/hi"Lucindalucine
E
10

The best way I have found to do this, is to use an in-line replace function like others have mentioned, and from whom I borrowed. Special shout out to @yannic-hamann for the regex and clear example. I am not worried about performance, as I am only doing this to construct paths.

I found my solution in MDN's docs.

const interpolateUrl = (string, values) => string.replace(/{(.*?)}/g, (match, offset) => values[offset]);

const path = 'theresalways/{what}/inthe/{fruit}-stand/{who}';
const paths = {
  what: 'money',
  fruit: 'banana',
  who: 'michael',
};

const expected = 'theresalways/money/inthe/banana-stand/michael';

const url = interpolateUrl(path, paths);

console.log(`Is Equal: ${expected === url}`);
console.log(`URL: ${url}`)
Emblematize answered 1/3, 2019 at 2:19 Comment(1)
Nice! I don't know how but this works. Magic I guess.Navarrette
J
6

Strings are immutable

Strings in JavaScript are immutable. It means that this will never work as you expect:

myString.replace(x, y);
alert(myString);

This is not just a problem with .replace() - nothing can mutate a string in JavaScript. What you can do instead is:

myString = myString.replace(x, y);
alert(myString);

Regex literals don't interpolate values

Regular expression literals in JavaScript don't interpolate values so this will still not work:

myString = myString.replace(/\{replaceArray[i]\}/gi, replaceWith[i]);

You have to do something like this instead:

myString = myString.replace(new RegExp('\{'+replaceArray[i]+'\}', 'gi'), replaceWith[i]);

But this is a little bit messy, so you may create a list of regexes first:

var regexes = replaceArray.map(function (string) {
    return new RegExp('\{' + string + '\}', 'gi');
});
for(var i = 0; i < replaceArray.length; i ++) {
  myString = myString.replace(regexes[i], replaceWith[i]);
}

As you can see, you can also use i < replaceArray.length instead of i <= replaceArray.length - 1 to simplify your loop condition.

Update 2017

Now you can make it even simpler:

var regexes = replaceArray.map(string => new RegExp(`\{${string}\}`, 'gi'));
for(var i = 0; i < replaceArray.length; i ++) {
  myString = myString.replace(regexes[i], replaceWith[i]);
}

Without a loop

Instead of looping and applying .replace() function over and over again, you can do it only once like this:

var mapping = {};
replaceArray.forEach((e,i) => mapping[`{${e}}`] = replaceWith[i]);
myString = myString.replace(/\{\w+\}/ig, n => mapping[n]);

See DEMO.

Templating engines

You are basically creating your own templating engine. If you want to use a ready solution instead, then consider using:

or something like that.

An example of what you are trying to do using Mustache would be:

var myString = "This is {{name}}'s {{adjective}} {{type}} in JavaScript! Yes, a {{type}}!";

var myData = {name: 'John', adjective: 'simple', type: 'string'};

myString = Mustache.to_html(myString, myData);

alert(myString);

See DEMO.

Jocelyn answered 17/3, 2011 at 2:59 Comment(1)
Currently I'm working with plop micro-generator which is a very useful templating library as well. See @ plopjs.comLarner
F
4

Here's a function that takes the string and an array of replacements. It's flexible enough to be re-used. The only catch is, you need to use numbers in your string instead of strings. e.g.,

var str = "{0} membership will start on {1} and expire on {2}.";

var arr = ["Jamie's", '11/27/14', '11/27/15'];

function personalizeString(string, replacementArray) {
  return string.replace(/{(\d+)}/g, function(match, g1) {
    return replacementArray[g1];
  });
}

console.log(
  personalizeString(str, arr)
)

Demo: https://jsfiddle.net/4cfy7qvn/

Fremont answered 27/11, 2014 at 0:33 Comment(1)
Just what I was looking for. It may be improved by using capture groups and changing ` {\d}` for {\d+} so you can use arbitrary length arrays. The result will look like this: const replace = (string, replacementArray) => string.replace(/{(\d+)}/g, (match, captureGroup) => replacementArray[captureGroup]); Ladino
G
2

I really like rsp's answer. Especially the 'Without a loop' section. Nonetheless, I find the code not that intuitive. I understand that this question comes from the two arrays scenario and that is more than 7 years old, but since this question appears as #1 on google when searching to replace a string with curly braces and the author asked for a similar setup I am tempted to provide another solution.

That being said, a copy and paste solution to play around with:

var myString = "This is {name}'s {adjective} {TYPE} in JavaScript! Yes, a { type }!";

var regex = /{(.*?)}/g;
myString.replace(regex, (m, c) => ({
    "name": "John",
    "adjective": "simple",
    "type": "string"
})[c.trim().toLowerCase()]);

This resource really helped me to build and understand the code above and to learn more about regex with JavaScript in general.

Gypsophila answered 27/8, 2018 at 9:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.