资讯专栏INFORMATION COLUMN

前端做模糊搜索

shadowbook / 1635人阅读

摘要:到目前为止我们只实现了搜索功能,按更优的体验来讲,在搜索结果中,要优先把相连匹配的放在首位,如关键字,要把结果放到前面。

我们先看一下效果图:

这是搜索关键字cfg时,会自动匹配到config方法

同样,我们再看另一个例子


通过关键字bi会匹配到好几个结果

这个和一些编辑器的搜索功能很像,比如sublime text,不需要知道关键字的完整拼写,只需要知道其中的几个字母即可。

那么这个功能在前端我们如何去实现呢?

不考虑性能的话,我们可以用正则简单实现如下:

把关键字拆分,加入(.?),如cfg最终为 (.?)(c)(.?)(f)(.?)(g)(.*?),然后拿这个正则去测试要搜索的列表,把符合要求的选项给拿出来即可

考虑到要高亮结果,我们还要生成对应的替换表达式,最后的函数如下

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) + "[sS]+?" + 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(keyword);
            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"]

对搜索结果中的内容做进一步处理渲染出来即可,比如把 ( 替换成 ) 替换成显示到页面上就完成了高亮显示

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95928.html

相关文章

  • UCloud控制台产品升级!

    摘要:主机接入新表格组件支持拖拽调整表头列宽背景现状客户对于主机名称的命名规则厂商地域可用区产品业务服务内网,名称较长,无法在列表页直观展示,只能手动上去显示全部。如有关于控制台产品的前端及视觉优化建议也欢迎和我们部门同学反馈。2月份根据用户反馈、用户调研以及自身使用产品体验,将主机列为重点体验优化目标产品,主要针对如下业务场景进行了优化:主机创建页可用区售罄地域切换优化主机列表页主机接入新表格组...

    ernest.wang 评论0 收藏0
  • 前端拼音模糊搜索

    摘要:前端实现拼音模糊搜索曾经沧海难为水除却巫山不是云曾我爱你中国我爱你中国爱你中 前端实现拼音模糊搜索pinyin-match[https://github.com/xmflswood/...[1] const PinyinMatch = require(pinyin-match);let test = 123曾经沧海难为水除却巫山不是云 PinyinMatch.match(test, 2...

    kel 评论0 收藏0
  • 前端拼音模糊搜索

    摘要:前端实现拼音模糊搜索曾经沧海难为水除却巫山不是云曾我爱你中国我爱你中国爱你中 前端实现拼音模糊搜索pinyin-match[https://github.com/xmflswood/...[1] const PinyinMatch = require(pinyin-match);let test = 123曾经沧海难为水除却巫山不是云 PinyinMatch.match(test, 2...

    fish 评论0 收藏0
  • 前端拼音模糊搜索

    摘要:前端实现拼音模糊搜索曾经沧海难为水除却巫山不是云曾我爱你中国我爱你中国爱你中 前端实现拼音模糊搜索pinyin-match[https://github.com/xmflswood/...[1] const PinyinMatch = require(pinyin-match);let test = 123曾经沧海难为水除却巫山不是云 PinyinMatch.match(test, 2...

    Apollo 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<