替换字符串中的值(如果它们存在于变量数组中)

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

具有以下对象数组:

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%`;

该函数应将以下字符串作为参数 variablesinputString 以及 return:

'Hi, my name is joe, I like tennis, you can contact me at joe@mail.com'

到目前为止的功能如下:

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

不幸的是,这只会找到一次,以防出现更多,并且它不会进入嵌套变量,例如 %EMAIL%.

有什么改进的想法吗?

您可以使用replaceAll方法替换所有出现的地方并循环两次。

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

我们可以在回调函数的帮助下进行正则表达式替换:

var variables = [
                   {name: '%NAME%', value: 'joe'},
                   {name: '%EMAIL%', value: 'joe@mail.com'},
                   {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);

这里的策略是首先从地图构建名称的正则表达式替换。我们对输入字符串中的这些名称进行全局正则表达式搜索。对于每个匹配项,回调函数都会将名称替换为地图中的值。

您可以进行递归调用,直到字符串中没有剩余变量

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

问题。

  • 字符串替换后您还没有更新 inputString

我使用了以下逻辑。

  • 首先遍历变量。
  • 如果任何值由变量数组本身的节点组成,则替换值(在我们的例子中,将 %NAME%@mail.com 替换为 joe@mail.com)
  • 使用这个替换数组来替换 inputString.
  • 中的字符串值
  • 使用 replaceAll 而不是 replace 来处理同一变量的多个变异。

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))

我使用“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));

另外,如果你愿意,你可以将我的代码修改为原型,如下所示:

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" }
  ])
)

通过数组向后循环,并使用 replaceAll() 应该可以解决问题。

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;
  variables.slice().reverse().forEach(variable => {
    output = output.replaceAll(variable.name, variable.value);
  })
  
  return output;  
}

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

使用正则表达式和 while(regex.test.output()) 也能达到目的。

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) {
  const regex = new RegExp("(" + variables.map(x => x.name).join("|") + ")", "g");
  let output = inputString;
  while (regex.test(output)) {
    output = output.replace(regex, m => {
      return variables.filter(x => x.name == m)[0].value;
    })
  }    
  
  return output;  
}

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

有点冗长的版本,但如果你需要在一次迭代中完成,迭代每个字符,当你得到第二个 %(意味着结束)时,然后寻找字符串的替换。

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))