【问题标题】:Javascript / jQuery faster alternative to $.inArray when pattern matching strings模式匹配字符串时,Javascript / jQuery 更快地替代 $.inArray
【发布时间】:2011-11-18 23:42:09
【问题描述】:

我有大量的 Javascript 单词数组(约 100,000 个),我希望能够根据文本模式快速返回其中的一个子集。

例如,我想返回所有以模式开头的单词,因此输入hap 应该会得到["happy", "happiness", "happening", etc, etc]

如果可能的话,我希望遍历整个数组。

这样的东西工作得不够快:

// data contains an array of beginnings of words e.g. 'hap'
$.each(data, function(key, possibleWord) {
 found = $.inArray(possibleWord, words);
 // do something if found
}

关于如何在不迭代整个单词集的情况下快速将集合减少到可能的匹配项的任何想法?如果有帮助,单词数组按字母顺序排列。

【问题讨论】:

  • hap 是否匹配 chap
  • 也许您可以将数组转换为字符串,然后使用正则表达式搜索该字符串。它更快吗?不知道,但值得一试。
  • 我不理解您现有的代码。 data 是 100,000 字的列表吗?如果是这样,words 是什么?你为什么在“循环”中使用$.inArray()
  • @ConradFrix 不,我想这是一个类似于hap*的搜索
  • @nnnnnn 更新了代码 - data 是一个单词开头的数组。我只是包含该代码以显示我所拥有的,并且不想在将来迭代 words

标签: javascript jquery arrays pattern-matching


【解决方案1】:

如果您只想搜索前缀,则有专门的数据结构,例如 Trie 和三元搜索树

一个快速的Google search 和一些有前途的 Javascrit Trie 和自动完成实现出现了:

http://ejohn.org/blog/javascript-trie-performance-analysis/

Autocomplete using a trie

http://odhyan.com/blog/2010/11/trie-implementation-in-javascript/

【讨论】:

    【解决方案2】:

    我完全不知道这是否有任何更快jsperf test 可能是为了...),但你可以用一个巨大的字符串和 RegExp 搜索而不是数组来做到这一点:

    var giantStringOfWords = giantArrayOfWords.join(' ');
    function searchForBeginning(beginning, str) {
        var pattern = new RegExp('\\b' + str + '\\w*'),
            matches = str.match(pattern);
        return matches;
    }
    
    var hapResults = searchForBeginning('hap', giantStringOfWords);
    

    【讨论】:

      【解决方案3】:

      最好的方法是更好地构建数据。用“hap”之类的键创建一个对象。该成员包含一个单词数组(或单词后缀,如果您想节省空间)或用于正则表达式搜索的分隔字符串。

      这意味着您将有更短的对象来迭代/搜索。另一种方法是对数组进行排序并使用二进制搜索模式。这里有一个关于技术和优化的很好的对话:http://ejohn.org/blog/revised-javascript-dictionary-search/

      【讨论】:

        【解决方案4】:

        我想使用原始 javascript 会有所帮助,你可以这样做:

        var arr = ["happy", "happiness", "nothere", "notHereEither", "happening"], subset = [];
        for(var i = 0, len = arr.length; i < len; i ++) {
             if(arr[i].search("hap") !== -1) {
                   subset.push(arr[i]);
             }
        }
        //subset === ["happy", "happiness","happening"]
        

        此外,如果数组是有序的,如果第一个字母大于搜索的第一个字母,则可以提前中断,而不是循环整个数组。

        【讨论】:

        • 您可能希望在搜索时执行arr[i].toLowerCase().search(query.toLowerCase()) !== -1 之类的操作,以获取不区分大小写的结果。
        【解决方案5】:
        var data = ['foo', 'happy', 'happiness', 'foohap'];    
        jQuery.each(data, function(i, item) {
              if(item.match(/^hap/))
                console.log(item) 
            });
        

        如果你有一个数组中的数据,你将不得不循环整个事情。

        【讨论】:

          【解决方案6】:

          一个非常简单的优化是在页面加载时检查你的大词数组,并记下适用于每个起始字母的索引范围。例如,在我下面的示例中,“a”字从 0 到 2,“b”字从 3 到 4,等等。然后在实际进行模式匹配时,只查看适用范围。虽然很明显有些字母会比其他字母包含更多的单词,但给定的搜索只需要平均查看 100,000/26 个单词。

          // words array assumed to be lowercase and in alphabetical order
          var words = ["a","an","and","be","blue","cast","etc."];
          
          // figure out the index for the first and last word starting with
          // each letter of the alphabet, so that later searches can use
          // just the appropriate range instead of searching the whole array
          var letterIndexes = {},
              i,
              l,
              letterIndex = 0,
              firstLetter;
          for (i=0, l=words.length; i<l; i++) {
              if (words[i].charAt(0) === firstLetter)
                 continue;
              if (firstLetter)
                  letterIndexes[firstLetter] = {first : letterIndex, last : i-1};
              letterIndex = i;
              firstLetter = words[i].charAt(0);
          }
          
          function getSubset(pattern) {
              pattern = pattern.toLowerCase()
              var subset = [],
                  fl = pattern.charAt(0),
                  matched = false;
              if (letterIndexes[firstLetter])
                  for (var i = letterIndexes[fl].first, l = letterIndex[fl].last; i <= l; i++) {
                      if (pattern === words[i].substr(0, pattern.length)) {
                          subset.push(words[i]);
                          matched = true;
                      } else if (matched) {
                          break;
                      }
                  }
              return subset;        
          }
          

          此外,如果您是在用户键入时执行此操作,则在将字母添加到模式末尾时,您只需搜索前一个子集,而不是整个列表。

          附:当然,如果您想按首字母分解单词列表,您可以轻松地在服务器端进行。

          【讨论】:

            猜你喜欢
            • 2013-10-13
            • 1970-01-01
            • 2012-04-27
            • 1970-01-01
            • 2011-03-19
            • 2015-02-04
            • 1970-01-01
            • 1970-01-01
            • 2014-03-22
            相关资源
            最近更新 更多