模糊搜索

咱們先看一下效果圖:css

這是搜索關鍵字 cfg時,會自動匹配到 config方法前端

一樣,咱們再看另外一個例子swift

經過關鍵字 bi會匹配到好幾個結果segmentfault

這個和一些編輯器的搜索功能很像,好比 sublime text,不須要知道關鍵字的完整拼寫,只須要知道其中的幾個字母便可。數組

那麼這個功能在前端咱們如何去實現呢?ruby

不考慮性能的話,咱們能夠用正則簡單實現以下:bash

把關鍵字拆分,加入 (.?),如 cfg 最終爲 (.?)(c)(.?)(f)(.?)(g)(.*?), 而後拿這個正則去測試要搜索的列表,把符合要求的選項給拿出來便可編輯器

考慮到要高亮結果,咱們還要生成對應的替換表達式,最後的函數以下svg

 
 
  
  
           
  
  
  1. 函數

var escapeRegExp = /[-#$^*()+[]{}|\,.?s]/g;var KeyReg = (key) => {var src = ['(.*?)('];var ks = key.split('');if (ks.length) {while (ks.length) {src.push(ks.shift().replace(escapeRegExp, '\$&'), ')(.*?)(');}src.pop();}src.push(')(.*?)');src = src.join('');var reg = new RegExp(src, 'i');var replacer = [];var start = key.length;var begin = 1;while (start > 0) {start--;replacer.push('$', begin, '($', begin + 1, ')');begin += 2;}replacer.push('$', begin);info = {regexp: reg,replacement: replacer.join('')};return info;};

調用 KeyReg把關鍵字傳入,拿返回值的 regexp去檢測搜索的列表,把符合的保存下來便可。

到目前爲止咱們只實現了搜索功能,按更優的體驗來說,在搜索結果中,要優先把相連匹配的放在首位,如 bi關鍵字,要把 bind結果放到 beginUpdate前面。第二個截圖是有優化的地方的。

要完成這個功能,咱們使用 KeyReg返回值中的 replacement,用它進行檢測,把結果中長度最長的放前面便可,這塊代碼之後有時間再補充

2018.5.31 今天重構了下,增長告終果排序,完整的代碼及使用示例以下

 
 
  
  
           
  
  
let Searcher = (() => {let escapeRegExp = /[-#$^*()+[]{}|\,.?s]/g;let escapeReg = reg => reg.replace(escapeRegExp, '\$&');//groupLeft 與 groupRight是對結果進一步處理所使用的分割符,能夠修改let groupLeft = '(',groupRight = ')';let groupReg = new RegExp(escapeReg(groupRight + groupLeft), 'g');let groupExtractReg = new RegExp('(' + escapeReg(groupLeft) + '[\s\S]+?' + escapeReg(groupRight) + ')', 'g');//從str中找到最大的匹配長度let findMax = (str, keyword) => {let max = 0;keyword = groupLeft + keyword + groupRight;str.replace(groupExtractReg, m => {//keyword完整的出如今str中,則優秀級最高,排前面if (keyword == m) {max = Number.MAX_SAFE_INTEGER;} else if (m.length > max) {//找最大長度max = m.length;}});return max;};let keyReg = key => {let src = ['(.*?)('];let ks = key.split('');if (ks.length) {while (ks.length) {src.push(escapeReg(ks.shift()), ')(.*?)(');}src.pop();}src.push(')(.*?)');src = src.join('');let reg = new RegExp(src, 'i');let replacer = [];let start = key.length;let begin = 1;while (start > 0) {start--;replacer.push('$', begin, groupLeft + '$', begin + 1, groupRight);begin += 2;}replacer.push('$', begin);info = {regexp: reg,replacement: replacer.join('')};return info;};return {search(list, keyword) {//生成搜索正則let kr = keyReg(userInput);let result = [];for (let e of list) {//若是匹配if (kr.regexp.test(e)) {//把結果放入result數組中result.push(e.replace(kr.regexp, kr.replacement).replace(groupReg, ''));}}//對搜索結果進行排序//1. 匹配關鍵字大小寫一致的優先級最高,好比搜索up, 結果中的[user-page,beginUpdate,update,endUpdate],update要排在最前面,由於大小寫匹配//2. 匹配關鍵字長的排在前面result = result.sort((a, b) => findMax(b, keyword) - findMax(a, keyword));return result;}};})();//假設list是待搜索的列表let list = ['config', 'user-page', 'bind', 'render', 'beginUpdate', 'update', 'endUpdate'];//假設userInput是用戶輸入的關鍵字let userInput = 'up';//獲取搜索的結果console.log(Searcher.search(list, userInput));// ["(up)date", "begin(Up)date", "end(Up)date", "(u)ser-(p)age"]

對搜索結果中的內容作進一步處理渲染出來便可,好比把 ( 替換成 <spanstyle="color:red"> 把 ) 替換成 </span>顯示到頁面上就完成了高亮顯示


做者:行列

https://segmentfault.com/a/1190000015486180

本文同步分享在 博客「grain先森」(JianShu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索