有不少種方法能實現數組濾重功能,有人統計過在 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"
複製代碼
具體過程以下:
好比圖中第一行中第二個b後面的綠色箭頭表示(?<=\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正則迷你書》。