10

I want to write a function to replace variables in a string with actual values, for example:

function replaceUrl(url, data) {}

The caller may be:

replaceUrl('/task/:module?taskId=:taskId#:hash', {
    module: 'm1',
    taskId: 't1',
    hash: 'h1'
});

I think to use RegEx may be simple to implement this, but I'm not very familiar with it.

Can anyone provide a simple way on this?

1
  • Library custom-string-formatter might be of help here, to avoid writing a parser yourself, and just focus on formatting values. Commented Aug 5 at 21:52

6 Answers 6

17

A simple solution is not to use RegEx at all. Use Template literals

var module = 'm1',
    taskId = 't1',
    hash   = 'h1';

var url = `/task/${module}?taskId=${taskId}#${hash}`;

var module = 'm1',
    taskId = 't1',
    hash = 'h1';

var url = `/task/${module}?taskId=${taskId}#${hash}`;

document.body.innerHTML = url;

Using RegEx:

function replaceUrl(url, data) {
    // Create regex using the keys of the replacement object.
    var regex = new RegExp(':(' + Object.keys(data).join('|') + ')', 'g');

    // Replace the string by the value in object
    return url.replace(regex, (m, $1) => data[$1] || m);
}

function replaceUrl(url, data) {
    var regex = new RegExp(':(' + Object.keys(data).join('|') + ')', 'g');

    return url.replace(regex, (m, $1) => data[$1] || m);
}


var updatedUrl = replaceUrl('/task/:module?taskId=:taskId#:hash', {
    module: 'm1',
    taskId: 't1',
    hash: 'h1'
});

console.log(updatedUrl);
document.body.innerHTML = updatedUrl;

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

1 Comment

Note: Template literals will throw reference error and stop processing, if the variable is not defined.
8

You could write a very simple templating function to achieve this in ES5:

function template(string, obj){
  var s = string;
  for(var prop in obj) {
    s = s.replace(new RegExp('{'+ prop +'}','g'), obj[prop]);
  }
  return s;
}

template('/task/{module}?taskId={taskId}#{hash}', {
  module: 'foo', 
  taskId: 2, 
  hash: 'bar'
});

Fiddle: https://jsfiddle.net/j5hp2cfv/

Comments

3

You can use the form of String.prototype.replace that takes a function as an argument.

Using your example this could look like:

var str = '/task/:module?taskId=:taskId#:hash&:missing';
var obj = {
    module: 'm1',
    taskId: 't1',
    hash: 'h1'
};

function replaceUrl(url, data) {
    var regex = /:(\w+)/g;
    return url.replace(regex, function(match, p1) {
        return data[p1] || ':' + p1;
    });
}

replaceUrl(str, obj); // /task/m1?taskId=t1#h1&:missing

This method will handle all parameters you pass values in for and ignore ones that are missing.

Comments

2

I wrote a simple JavaScript function called stringInject to solve this exact problem I was having. It would allow you to do the following. Find links to the code below.

var str = "This is a {0} string for {1}"
var newStr = stringInject(str, ["test", "stringInject"]);

// This is a test string for stringInject 

NPM / GitHub links:

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

https://github.com/tjcafferkey/stringinject

The Function

function stringInject(str, arr) {
    if (typeof str !== 'string' || !(arr instanceof Array)) {
        return false;
    }

    return str.replace(/({\d})/g, function(i) {
        return arr[i.replace(/{/, '').replace(/}/, '')];
    });
}

Comments

0

You can use the following function

function replaceStringVariable(text, data) {
  // Create regex using the keys of the replacement object.
  const regex = new RegExp('\\${(' + Object.keys(data).join('|') + ')}', 'g')
  
  // Replace the string by the value in object
  return text.replace(regex, (m, p1) => data[p1] || m)
}

const newText = replaceStringVariable('hello ${name} and ${nameTwo}', {
  name: "Pedro",
  nameTwo: "Juan"
})

console.log('newText', newText)

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0

I ran into the same issue, so I’ve implemented a string-extension method to handle this requirement. I’ve also covered most edge cases in my tests, so you can use it confidently.


Utility functions

const isNull = obj => obj === null;
const isUndefined = obj => typeof obj === 'undefined';
const isNullOrUndefined = obj => isUndefined(obj) || isNull(obj);
const isObject = obj =>
  !isNullOrUndefined(obj) &&
  typeof obj === 'object' &&
  !Array.isArray(obj);

function stringify(data) {
  switch (typeof data) {
    case 'undefined':
      return 'undefined';
    case 'boolean':
      return data ? 'true' : 'false';
    case 'number':
      return String(data);
    case 'string':
      return data;
    case 'symbol':
    case 'function':
      return data.toString();
    case 'object':
      if (isNull(data)) {
        return 'null';
      }
      if (data instanceof Error) {
        return data.toString();
      }
      if (data instanceof Date) {
        return data.toISOString();
      }
      return JSON.stringify(data, null, 2);
    default:
      return 'unknown';
  }
}

function escapeRegex(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

function hasFunction(obj, method) {
  return (
    isObject(obj) &&
    method in obj &&
    typeof obj[method] === 'function'
  );
}

replaceVariable extension

String.prototype.replaceVariable = function (replacements, prefix = '%#', suffix = '#%') {
  let current = this.toString();
  const seen = new Set();

  // Escape prefix/suffix in case they contain regex metacharacters
  prefix = escapeRegex(prefix);
  suffix = escapeRegex(suffix);

  // Build regex patterns for each placeholder
  const patterns = Object.keys(replacements).map(key => {
    const escKey = escapeRegex(key);
    return {
      value: replacements[key],
      placeholderRegex: new RegExp(`${prefix}${escKey}(?=(?::.*?${suffix}|${suffix}))(?::.*?)?${suffix}`, 'gs'),
      formatRegex: new RegExp(`(?<=${prefix}${escKey}(?=(?::.*?${suffix}|${suffix})):).*?(?=${suffix})`, 'gs')
    };
  });

  // Repeat until no more replacements occur or we detect a loop
  while (true) {
    if (seen.has(current)) break;
    seen.add(current);
    let next = current;
    for (const {value, placeholderRegex, placeholderFormatRegex} of patterns) {
      if (placeholderRegex.test(next)) {
        let format = next.match(placeholderFormatRegex)
        if (!isNullOrUndefined(format) && format.length != 0 && hasFunction(value, 'format')) {
          next = next.replace(placeholderRegex, stringify(value.format(format[0])));
        } else {
          next = next.replace(placeholderRegex, stringify(value));
        }
      }
    }
    if (current === next) break;
    current = next;
  }
  return current;
};

Example format method for Date

Date.prototype.format = function (fmt) {
  const pad = num => num.toString().padStart(2, '0');
  const map = {
    YYYY: this.getFullYear(),
    MM: pad(this.getMonth() + 1),
    DD: pad(this.getDate()),
    HH: pad(this.getHours()),
    mm: pad(this.getMinutes()),
    ss: pad(this.getSeconds()),
  };

  return fmt.replace(/YYYY|MM|DD|HH|mm|ss/g, key => map[key]);
};

Usage example

console.log('Hello {{name}}! {{time:(YYYY-MM-DD HH:mm:ss)}}'.replaceVariable({
  name: 'World',
  time: new Date('2025-05-09T12:28:12'),
}, '{{', '}}'));
// → Hello World! (2025-05-09 12:28:12)

2 Comments

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.
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.