未加星标

正则实现数组滤重

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二05 | 时间 2019 | 作者 红领巾 ] 0人收藏点击收藏

有很多种方法能实现数组滤重功能,有人统计过在 JS 里至少就有 10 种方式。

本文关心的是:能否用正则来实现滤重这个功能呢?

诚然,就算能实现,估计也没人会把它当成最佳实践的。

所以这里,我们只考虑可能性。

本文给出的答案: 可以 !而且不止一种方式。

下面我们从易到难一步步来看如何实现的。

1. 相邻字符滤重问题 "abbccc" => "abc"

正则里要匹配之前出现过的字符,需要使用反向引用:

function distinct(string) { return string.replace(/(.)\1+/g, '$1') } console.log(distinct("abbccc")) // => "abc" 复制代码

其中 \1 是反向引用,指代第一个括号捕获的数据,其中称为 (.) 为捕获分组。而 $1 也表示第一个括号捕获的数据。具体过程请看下图。


正则实现数组滤重

其中蓝色表示捕获分组捕获到的数据,粉色的表示反向引用指代的数据。进行替换操作后带颜色的数据只保留了蓝色数据。

2. 字符串滤重 "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" 复制代码

具体过程如下:


正则实现数组滤重
比如图中第一行中第二个b后面的绿色箭头表示 (?<=\1.*?\1) 。第一个 \1 是粉色 b

,第二个是蓝色的那个。

3. 数组滤重

有字符串滤重后,数组滤重就简单了。上面四种方法都可以写成数组版本的。比如第四种方案如下:

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正则迷你书》。

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

代码区博客精选文章
分页:12
转载请注明
本文标题:正则实现数组滤重
本站链接:https://www.codesec.net/view/628528.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(68)