有不少种方法能实现数组滤重功能,有人统计过在 JS
里至少就有 10
种方式。数组
本文关心的是:可否用正则来实现滤重这个功能呢?bash
诚然,就算能实现,估计也没人会把它当成最佳实践的。post
因此这里,咱们只考虑可能性。ui
本文给出的答案:能够!并且不止一种方式。spa
下面咱们从易到难一步步来看如何实现的。3d
"abbccc" => "abc" code
正则里要匹配以前出现过的字符,须要使用反向引用:cdn
function distinct(string) {
return string.replace(/(.)\1+/g, '$1')
}
console.log(distinct("abbccc"))
// => "abc"
复制代码
其中 \1
是反向引用,指代第一个括号捕获的数据,其中称为 (.)
为捕获分组。而 $1
也表示第一个括号捕获的数据。具体过程请看下图。blog
其中蓝色表示捕获分组捕获到的数据,粉色的表示反向引用指代的数据。进行替换操做后带颜色的数据只保留了蓝色数据。排序
"abbacbc" => "abc"
通常的字符串这么办呢?
最直接的思路是把问题转化为已解决过的问题。
把字符串拆分红数组,而后字节码排序,转化成相邻字符滤重问题。
这种方式,用了数组相关方法,正则的意味就没那么浓烈了。
使用循环,删除重复出现的字符。
function distinct(string){
while(/(.).*?\1/.test(string)) {
string = string.replace(/(.)(.*?)\1/, '$1$2')
}
return string;
}
console.log(distinct("abbacbc"))
// => "abc"
复制代码
用正则 /(.).*?\1/
来判断字符串里是否还有重复字符,有的话,就替换一下。 替换的正则是 /(.)(.*?)\1/
,其中使用了两组括号,为引用 $1
和 $2
提供了数据。具体过程示图以下:
方式二里使用了循环,总以为有点太笨。其实能够直接使用 replace
。此时须要使用 (?=p)
:
function distinct(string) {
return string.replace(/(.)(?=.*?\1)/g, '')
}
console.log(distinct("abbacbc"))
// => "abc"
复制代码
具体过程示图以下:
(?=.*?\1)
表示匹配位置,即图中绿色箭头所示。如第一行中字符 a
后面的位置,改位置后面的字符匹配 .*?\1
,其中 \1
即图中粉色的数据,对应于第一个分组捕获的蓝色数据。最后全部的蓝色数据都被替换成 ''
了。
这种实现方式有一个问题,就是重复字符只保留最后出现的字符。若是在原来字符串后面加个 "a"
变成 "abbacbca"
,最终结果倒是 "bca"
。
方式三的思路是看当前字符是否会在后面出现,若是出现就删除。方式四的逻辑却能够说反过来的:若是当前字符在前面出现过,那么就删除。此时须要用断言 (?<=p)
,看当前位置前面是否匹配 p
。
正则不能想固然地写成 /(?<=.*?\1)(.)/g
,由于 \1
是“反向”引用,只能引用它以前的分组。因此这里要把它放在目标字符后面:
function distinct(string) {
return string.replace(/(.)(?<=\1.*?\1)/g, '')
}
console.log(distinct("abbacbc"))
// => "abc"
复制代码
具体过程以下:
(?<=\1.*?\1)
。第一个
\1
是粉色
b
,第二个是蓝色的那个。
有字符串滤重后,数组滤重就简单了。上面四种方法均可以写成数组版本的。好比第四种方案以下:
function distinct(arr) {
return arr.join('').replace(/(.)(?<=\1.*?\1)/g, '').split('')
}
console.log(distinct(['a','b','b','a','c','b','c']))
// => ['a', 'b', 'c']
复制代码
至此咱们的解决方案还有一些问题:
支持多位字符相对容易解决,可是要保持类型的话,须要JSON两个方法了。
最后给出方案四的最终版本:
function distinct(arr) {
var string = JSON.stringify(arr)
string = string.replace(/,([^,]+)(?<=\1.*?\1)(?=,|])/g, (m, $1) => $1 == '"' ? m : '')
return JSON.parse(string)
}
console.log(distinct(["aa",1,"ab",true,1,true,"aa"]))
// => ["aa", 1, "ab", true]
复制代码
本文完。
另外,欢迎阅读本人的《JS正则迷你书》。