【问题标题】:How to replace only first sequential occurences (fuzzymatch)?如何仅替换第一个连续出现(模糊匹配)?
【发布时间】:2012-06-02 03:24:50
【问题描述】:

我正在尝试编写“模糊”匹配,但找不到解决此问题的方法:

数据输入:makrusakkk,查询:mrk,预期结果:<b>m</b>ak<b>r</b>usa<b>k</b>kk

正则表达式:"makrusakkk".match(/(m).*?(r).*?(k)/i) 返回["makrusak", "m", "r", "k"]

所以问题是:有没有办法使用 RegExp 获得预期的结果?

【问题讨论】:

  • 这个问题似乎可以通过简单的循环来解决。为什么是正则表达式?

标签: javascript regex fuzzy-search


【解决方案1】:

我认为对此类问题使用正则表达式会使事情变得更加复杂。以下基于字符串和循环的解决方案将导致结果:

function fuzzySearch(query, input) {
    var inds = patternMatches(query, input);
    if(!inds) return input;

    var result = input;
    for(var i = inds.length - 1; i >= 0; i--) {
        var index = inds[i];
        result = result.substr(0,index) + 
            "<b>" + result[index] + "</b>" + 
            result.substr(index+1);
    }

    return result;
}

function patternMatches(query, input) {
    if(query.length <= 0) {
        return [];
    } else if(query.length == 1) {
        if(input[0] == query[0]) return [0];
        else return [];
    } else {
        if(input[0] != query[0])
        return false;

        var inds = [0];
        for(var i = 1; i < query.length; i++) {
            var foundInd = input.indexOf(query[i], inds[i-1]);
            if(foundInd < 0) {
                return [];
            } else {
                inds.push(foundInd);
            }
        }
        return inds;        
    }
}

var input = "makrusakkksd";
var query = "mrk";
console.log(fuzzySearch(query, input));
console.log(patternMatches(query, input));

这里还有一个现场演示:http://jsfiddle.net/sinairv/T2MF4/

【讨论】:

  • 非常好的替代解决方案。一个解决方法:输入是 makrusakkk(三倍 K),您的代码标记 &lt;b&gt; 最后出现 K,但应该首先
  • 这是有目的的。我认为“mrk”的目的是过滤以“m”开头、以“k”结尾并在其间有“r”的单词。标记“k”的第一次出现使代码更加简洁明了!
  • 好的,谢谢。它不是 RegExp,但它适合需求。标记为解决方案。
  • @mjey 用正则表达式解决这个问题就像上面一样,但根本不是一个明智的解决方案。我们必须先执行相同的基于字符串的循环来生成正则表达式。然后在找到匹配项之后,我们必须再次像上面一样遍历找到的匹配组以形成结果。与上述解决方案相比,它需要两倍的努力,并且运行时间更长。
【解决方案2】:

这里你需要for:

function search_for_it(txt, arr){
    for(i=0;i<arr.length;i++){
        var reg = new RegExp(arr[i], "i");
        txt = txt.replace(reg, "<b>"+arr[i]+"</b>");
    }
    return txt;
}

search_for_it("makrusakkk", ["m","r","k"]);

//return "<b>m</b>a<b>k</b><b>r</b>usakkk"

PS:您的预期结果不正确。在第一个a 之后有一个k

【讨论】:

  • 认为OP的意思是“mrk”要按顺序处理,因此第一个“k”不考虑。
【解决方案3】:

有没有办法使用 RegExp 获得预期结果?

有。

"makrusakkk".replace(/(m)(.*?)(r)(.*?)(k)/i, '<b>$1</b>$2<b>$3</b>$4<b>$5</b>'​​​​​​​)

【讨论】:

  • 嗯。如果我们进行手动替换,这很简单,但我需要 function - 例如输入参数每次都不一样。
【解决方案4】:

我对此隐约感到肮脏,但是……无论如何;这是一个的方法:

$('#s').keyup(

function(e) {
    var w = e.which;
    if (w == 8 || w == 46) {
        return false;
    }
    var listElems = $('ul:first li'),
        search = $(this).val().replace(/w+/g, ''),
        r = search.split(''),
        rString = [];
    $.each(r, function(i, v) {
        rString.push('(' + v + ')');
    });
    var reg = new RegExp(rString.join('(\\d|\\D)*'), 'gi');

    listElems.each(

    function() {
        if (!$(this).attr('data-origtext')) {
            $(this).attr('data-origtext', $(this).text());
        }
        $(this).html($(this).attr('data-origtext').replace(reg, '<b>$&</b>'));
    });
});​

JS Fiddle demo.

几乎可以肯定,它可以从相当多的简化中受益。

参考资料:

【讨论】:

    猜你喜欢
    • 2016-09-08
    • 2020-12-09
    • 1970-01-01
    • 2019-08-24
    • 2021-04-16
    • 2022-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多