正则表达式——匹配范围,但每个唯一字符只匹配一次

Regex — Match Range but only once per unique character

我试图通过使用正则表达式范围来避免数组和循环,但是,我只想替换范围内每个字符的第一个实例一次。

我使用范围是因为我不能保证顺序并且不能重新排序。

例如:

"access".replace(/[access]/g, '') = "cs", instead of "". "cell phones".replace(/[el]/g) = "cl phones", instead of "c phons"

正则表达式范围内的双打无论如何都是多余的,在这种情况下,它也不应该导致第二次出现被替换。

如果这不可能,那我就得想别的办法了。

这需要向后看 -- 查看该字符是否更早出现并因此应保留。但是 JS 不支持。搜索 "JS look-behind regexp" 以获得一些想法。经典方法是反转字符串,以便您可以使用前瞻:

const rev = s => s.split('').reverse().join('');

const testData = [
  ["access", 'access'],
  ["cell phones", 'el']
];

function match(s, chrs) {
  const reg = new RegExp(`([${chrs}])(?!.*\1)`, "g");
  return rev(rev(s).replace(reg, ''));
}
  
testData.forEach(([input, chrs]) => console.log("input", input, "gives", match(input, chrs)));

以我在上面评论中所说的为例:

function replace(str, regex) {
  var cache = {};          // the cache object
  return str.replace(regex, function(m) {
    if(cache[m]) return m; // if we already removed an instance of this matched character then don't remove any further instances
    
    // otherwise ...
    cache[m] = true;       // cache the character
    return '';             // remove it
  });
}

console.log('"access", /[access]/g => ', replace("access", /[access]/g));
console.log('"cell phones", /[el]/g => ', replace("cell phones", /[el]/g));

注意:假设传递的正则表达式都是字符集(ranges as you call them),只允许/[...]/g,否则行为获胜随心所欲

"remembering" 逻辑的单行版本。

"cell phones".replace(/[el]/g, (() => { let seen = []; return m => seen.includes(m) ? m : (seen.push(m),''); })())

更短的版本:

"cell phones".replace(/[el]/g, (seen => m => m in seen ? m : seen[m] = ''))({}))