regex JavaScript -替换map数组中的字符串,如果它匹配map的任何正则表达式数组

q8l4jmvw  于 2023-04-07  发布在  Java
关注(0)|答案(1)|浏览(99)

我有下面两个变量regexMapoutputMap如下

var regexMap = new Map([
            ['valOne\d{6}', 'regexvalue1'],
            ['valTwo\d{4}', 'regexvalue2']
        ])

var outputMap = [
            new CustomObj({
             'key1': 'value1',
             'key2': 'value2',
             'key3': 'A sentence with valOne123456 and valTwo2345'
            }),
            new CustomObj({
             'key1': 'value1',
             'key2': 'value2',
             'key3': 'A sentence with valTwo1234'
            }),
            new CustomObj({
             'key1': 'value1',
             'key2': 'value2',
             'key3': 'A sentence with value3'
            })
        ]

我想检查outputMap中特定键的值(例如:如果该值与regexMap中的任何键匹配,则将其替换为regexMap中的值(例如:如果value3regex1和/或regex2匹配,则分别用regexvalue1和/或regexvalue2替换它们)
对于上面的例子输入,我想要的输出是下面的对象

[
            new CustomObj({
             'key1': 'value1',
             'key2': 'value2',
             'key3': 'A sentence with regexvalue1 and regexvalue2'
            }),
            new CustomObj({
             'key1': 'value1',
             'key2': 'value2',
             'key3': 'A sentence with regexvalue2'
            }),
            new CustomObj({
             'key1': 'value1',
             'key2': 'value2',
             'key3': 'A sentence with value3'
            })
        ]

我如何实现这一点?

2ic8powd

2ic8powd1#

首先,regexMap不需要使用Map对象。原因是只有当你要查找键来获取值时,map才有意义。这里的结构表示一个 *regex规则 *,它有一个 regex 来测试,当regex匹配时,它有一个 replacement。如果这表示为map,它永远不会被正则表达式用来查找,因为需要遍历整个数据结构才能得到匹配的规则。
更明智的方法是直接拥有一个regex规则列表:

const regexRules = [
    { regex: "valOne\\d{6}", replacement: 'replacementValue1' },
    { regex: "valTwo\\d{4}", replacement: 'replacementValue2' },
];

或者直接使用正则表达式:

const regexRules = [
    { regex: /valOne\d{6}/, replacement: 'replacementValue1' },
    { regex: /valTwo\d{4}/, replacement: 'replacementValue2' },
];

注意字符串需要对反斜杠进行额外的转义。
有了这个简单的替换引擎可以定义为

const runRules = value => {
  let result = value;
  
  //replace anything that matches the `regex` part of the rule
  //with the `replacement` part of the rule
  for(const { regex, replacement } of regexRules)
    result = result.replaceAll(new RegExp(regex, "g"), replacement);
  
  return result;
};

注意,这会为每个规则显式创建一个新的regex对象。(当反斜杠被正确转义时)以及正则表达式字面量。构造函数将克隆正则表达式模式并强制正则表达式的全局标志以替换所有出现。即使正则表达式模式是用g标志(如{ regex: /valOne\d{6}/g, /* .... */ })定义的,也需要进行克隆,因为全局正则表达式对象是 * 有状态的 *,并且在匹配之间重用它们会导致不正确的输出。
剩下的工作可以通过定义一个函数来完成,该函数对键的子集应用所有规则:

const exchangeValues = (keysToCheck, obj) => {
  //only run over the supplied keys
  for(const key of keysToCheck) {
    //make sure the value can be replaced
    if (key in obj && typeof obj[key] === "string")
      obj[key] = runRules(obj[key]);
  }
}

使用此设置,处理数据的代码如下所示:

const regexRules = [
    { regex: /valOne\d{6}/, replacement: 'replacementValue1' },
    { regex: /valTwo\d{4}/, replacement: 'replacementValue2' },
];

const runRules = value => {
  let result = value;
  
  for(const { regex, replacement } of regexRules)
    result = result.replaceAll(new RegExp(regex, "g"), replacement);
  
  return result;
};
  
const exchangeValues = (keysToCheck, obj) => {
  for(const key of keysToCheck) {
    if (key in obj && typeof obj[key] === "string")
      obj[key] = runRules(obj[key]);
  }
}

output.forEach(obj => exchangeValues(["key3"], obj));

console.log(output);
.as-console-wrapper { max-height: 100% !important; }
<script>
  //setup the data
  class CustomObj {
    constructor({key1, key2, key3}) { 
      this.key1 = key1; 
      this.key2 = key2; 
      this.key3 = key3; 
    }
  }

  const output = [
      new CustomObj({ 'key1': 'value1', 'key2': 'value2', 
        'key3': 'A sentence with valOne123456 and valTwo2345' }),
      new CustomObj({ 'key1': 'value1', 'key2': 'value2', 
        'key3': 'A sentence with valTwo1234' }),
      new CustomObj({ 'key1': 'value1', 'key2': 'value2', 
        'key3': 'A sentence with value3' })
  ];
</script>

为了整理代码并使其更具可重用性,可以将其定义为一个库,该库将规则参数化,因此可以使用不同的规则进行调用。例如,regexRules可以从配置中加载,而无需更改使用它们的库代码。
此外,可以通过使用Function#bindpartially applying参数来简化使用:

/* library code */
const ruleApplier = (rules, value) => {
  let result = value;
  
  for(const { regex, replacement } of regexRules)
    result = result.replaceAll(new RegExp(regex, "g"), replacement);
  
  return result;
};
  
const exchangeValues = (runRules, keysToCheck, obj) => {
  for(const key of keysToCheck) {
    if (key in obj && typeof obj[key] === "string")
      obj[key] = runRules(obj[key]);
  }
}
/* / library code */

const regexRules = [
    { regex: /valOne\d{6}/, replacement: 'replacementValue1' },
    { regex: /valTwo\d{4}/, replacement: 'replacementValue2' },
];

const process = exchangeValues.bind(
  null,
  ruleApplier.bind(null, regexRules),
  ["key3"],
)

output.forEach(process);

console.log(output);
.as-console-wrapper { max-height: 100% !important; }
<script>
  //setup the data
  class CustomObj {
    constructor({key1, key2, key3}) { 
      this.key1 = key1; 
      this.key2 = key2; 
      this.key3 = key3; 
    }
  }

  const output = [
      new CustomObj({ 'key1': 'value1', 'key2': 'value2', 
        'key3': 'A sentence with valOne123456 and valTwo2345' }),
      new CustomObj({ 'key1': 'value1', 'key2': 'value2', 
        'key3': 'A sentence with valTwo1234' }),
      new CustomObj({ 'key1': 'value1', 'key2': 'value2', 
        'key3': 'A sentence with value3' })
  ];
</script>

然而,对于.bind(),第一个参数应该总是被传递(null在这里是可以的),但它有点烦人。可以通过curry参数来实现更流畅的使用。

/* library code */
const ruleApplier = rules => value => {
  let result = value;
  
  for(const { regex, replacement } of regexRules)
    result = result.replaceAll(new RegExp(regex, "g"), replacement);
  
  return result;
};

//accept varargs of keys to check for simplicity
const exchangeValues = runRules => (...keysToCheck) => obj => {
  for(const key of keysToCheck) {
    if (key in obj && typeof obj[key] === "string")
      obj[key] = runRules(obj[key]);
  }
}
/* / library code */

const regexRules = [
    { regex: /valOne\d{6}/, replacement: 'replacementValue1' },
    { regex: /valTwo\d{4}/, replacement: 'replacementValue2' },
];

const process = exchangeValues (ruleApplier(regexRules)) ("key3");

output.forEach(process);

console.log(output);
.as-console-wrapper { max-height: 100% !important; }
<script>
  //setup the data
  class CustomObj {
    constructor({key1, key2, key3}) { 
      this.key1 = key1; 
      this.key2 = key2; 
      this.key3 = key3; 
    }
  }

  const output = [
      new CustomObj({ 'key1': 'value1', 'key2': 'value2', 
        'key3': 'A sentence with valOne123456 and valTwo2345' }),
      new CustomObj({ 'key1': 'value1', 'key2': 'value2', 
        'key3': 'A sentence with valTwo1234' }),
      new CustomObj({ 'key1': 'value1', 'key2': 'value2', 
        'key3': 'A sentence with value3' })
  ];
</script>

参见:

相关问题