'Replace values from a string if they are present in an array of variables

Having the following array of objects:

const variables = [
  { name: '%NAME%', value: 'joe' },
  { name: '%EMAIL%', value: '%NAME%@mail.com' },
  { name: '%HOBBY%', value: 'tennis' }
];

And the input string:

const inputString = `Hi, my name is %NAME%, I like %HOBBY%, you can contact me at %EMAIL%`;

The function should take as arguments variables and inputString and return the following sting:

'Hi, my name is joe, I like tennis, you can contact me at [email protected]'

Here is the function so far:

function doMagic(variables, inputString) {
  let output = inputString;
  for (let i = 0; i < variables.length; i++) {
     output = inputString.replace(variables[i].name, variables[i].value);
  }
  return output;
}

Unfortunatelly, this only finds one occurrence, in case there are more, and it doesn't go into nested variables, like %EMAIL%.

Any ideas to improve?



Solution 1:[1]

You could do a recursive call until there are no variables left in the string

const variables = [
  { name: '%NAME%', value: 'joe' },
  { name: '%EMAIL%', value: '%NAME%@mail.com' },
  { name: '%HOBBY%', value: 'tennis' },
];

const inputString = `Hi, my name is %NAME%', I like %HOBBY%, you can contact me at %EMAIL%`;

function doMagic(variables, inputString) {
  let output = inputString;
  for (let i = 0; i < variables.length; i++) {
    output = output.replace(variables[i].name, variables[i].value);
  }
  for (let i = 0; i < variables.length; i++) {
    if (output.includes(variables[i].name)) {
      output = doMagic(variables, output);
    }
  }
  return output;
}

console.log(doMagic(variables, inputString));

Solution 2:[2]

We can do a regex replacement with the help of a callback function:

var variables = [
                   {name: '%NAME%', value: 'joe'},
                   {name: '%EMAIL%', value: '[email protected]'},
                   {name: '%HOBBY%', value: 'tennis'}
                  ];
var regex = new RegExp("(" + variables.map(x => x.name).join("|") + ")", "g");
var inputString = "Hi, my name is %NAME%, I like %HOBBY%, you can contact me at %EMAIL%";
var output = inputString.replace(regex, (m) => {
    return variables.filter(x => x.name == m)[0].value;
});
console.log(output);

The strategy here is to first build a regex alternation of names from the map. We do a global regex search for these names in the input string. For each match, the callback function replaces the name with the value from the map.

Solution 3:[3]

Issues.

  • You have not updated inputString after string replace.

I have made use of below logic.

  • Loop through variables first.
  • Replace the values if any of the value consist of node from variable array itself (in our case replace %NAME%@mail.com as [email protected])
  • Use this replaced array to perform replacement of string values from inputString.
  • Use replaceAll instead of replace to handle multiple ovvurance of same variable.

const variables = [
  { name: '%NAME%', value: 'joe' },
  { name: '%EMAIL%', value: '%NAME%@mail.com' },
  { name: '%HOBBY%', value: 'tennis' }
];
const inputString = `Hi, my name is %NAME%', I like %HOBBY%, you can contact me at %EMAIL%`;
function doMagic(variables, inputString) {
  let output = inputString;
  // First replace variables
  for (let i = 0; i < variables.length; i++) {
    const node = variables.find(item => item.value.includes(variables[i].name));
    if (node) {
      node.value = node.value.replaceAll(variables[i].name, variables[i].value);
    }
  }
  for (let i = 0; i < variables.length; i++) {
    output = output.replaceAll(variables[i].name, variables[i].value);
  }
  return output;
}
console.log(doMagic(variables, inputString))

Solution 4:[4]

You can use replaceAll method to replace all the occurrence and loop twice.

function doMagic(variables, inputString) {
  let output = inputString;
  for (let i = 0; i < variables.length; i++) {
     output = output.replaceAll(variables[i].name, variables[i].value);
  }
  for (let i = 0; i < variables.length; i++) {
     output = output.replaceAll(variables[i].name, variables[i].value);
  }
  return output;
}

Solution 5:[5]

My solution with use "reduce"

const variables = [
  { name: "%NAME%", value: "joe" },
  { name: "%EMAIL%", value: "%NAME%@mail.com" },
  { name: "%HOBBY%", value: "tennis" }
];
const inputString =
  "Hi, my name is %NAME%, I like %HOBBY%, you can contact me at %EMAIL%";

function doMagic(string, variables) {
  const replaceVar = (s, v) =>
    v.reduce((prev = "", { name, value }) => prev.replace(name, value), s);
  const fixedVariables = variables.map((v) => ({
    ...v,
    value: replaceVar(v.value, variables)
  }));

  return replaceVar(string, fixedVariables);
}

console.log(doMagic(inputString, variables));

Also if you want, you can modify my code to prototype, like this:

const inputString =
  "Hi, my name is %NAME%, I like %HOBBY%, you can contact me at %EMAIL%";

String.prototype.doMagic = function (variables) {
  const replaceVar = (s, v) =>
    v.reduce((prev = "", { name, value }) => prev.replace(name, value), s);
  const fixedVariables = variables.map(({ value, ...rest }) => ({
    ...rest,
    value: replaceVar(value, variables)
  }));

  return replaceVar(this, fixedVariables);
};


console.log(
  inputString.doMagic([
    { name: "%NAME%", value: "joe" },
    { name: "%EMAIL%", value: "%NAME%@mail.com" },
    { name: "%HOBBY%", value: "tennis" }
  ])
)

Solution 6:[6]

Bit lengthy version, But if you need to do in one iteration, iterate over each character and when you get second % (means ending) then look for replacement of string.

const variables = [
  { name: "%NAME%", value: "joe" },
  { name: "%EMAIL%", value: "%NAME%@mail.com" },
  { name: "%HOBBY%", value: "tennis" },
];

const inputString = `Hi, my name is %NAME%, I like %HOBBY%, you can contact me at %EMAIL%`;

const cache = variables.reduce((acc, { name, value }) => ({
  ...acc,
  [name]: value,
}), {});

const myReplace = (str) => {
  let newString = "";
  let count = 0;
  let last = 0;
  let i;
  
  for (i = 0; i < str.length; i++) {
    if (str[i] === "%") {
      if (count === 0) {
        newString = `${newString}${str.slice(last, i)}`;
        count += 1;
        last = i;
      } else {
        newString = `${newString}${myReplace(cache[str.slice(last, i + 1)])}`;
        count = 0;
        last = i + 1;
      }
    }
  }
  newString = `${newString}${str.slice(last, i)}`;
  return newString;
};

console.log(myReplace(inputString))

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 hgb123
Solution 2 Leo Messi
Solution 3 Nitheesh
Solution 4
Solution 5 Kordrad
Solution 6 Siva K V