通过Javascript数组快速循环

时间:2012-07-13 19:43:42

标签: javascript

在我的JSP页面上,我有一个可以包含60k +对象的select对象。我将这些60k +对象存储到名为“masterList”的javascript数组中。我已经为用户提供了一个输入框来过滤列表。过滤基于“以...开头”方法。有更快的方法吗?当用户在输入框中输入零或1个字符时,我注意到了性能问题。

这就是我的代码现在的样子。

    var numShown = 0;
    var listLength = masterList.length;     

    for(i = 0; i < listLength; i++){
        if(masterList[i].search(re) != -1){
            selectBox[numShown] = new Option(masterList[i], masterList[i]);
            numShown++;
        }

        // Stop when the number to show is reached and input present
        if(input.value != "" && numShown == maxToShow){
            break;
        }
    }

7 个答案:

答案 0 :(得分:2)

每次循环创建的Option()对象是什么?这本身可能是性能问题的根源。您不太可能需要一直创建新对象,因此如果您可以添加该代码,则有助于优化代码。

开始优化搜索时间的一个非常基本的方法是创建第二个对象,其中包含masterList中每个字母的起始索引。请注意,如果masterList数据在用户使用时发生更改,则需要重新计算索引对象。

e.g。

var masterList = [aardvark, apple, balloon, blue, cat, dog];
var indexes = {'a':0, 'b':2, 'c':4, 'd':5};

每次用户输入时,取出他们输入的第一个字符,并在'indices'对象中引用其起始索引。然后从该索引开始for循环。另请记住,当您没有找到匹配项时,必须停止for循环,或者将搜索扩展到字母匹配范围之外。基本上,当您的初始搜索条件不符合时,搜索一直到masterList的结尾是没有意义的。

其他说明: 您需要在for循环中使用var声明我。 无论你是否知道,你都通过listLength缓存长度做了一件好事。否则Javascript会在每次迭代时重新计算masterList.length。

答案 1 :(得分:1)

JQuery UI为您提供a good autocompletion component,如果您将60k元素推送到客户端,可以认为您有设计缺陷。

此组件使您能够:

  • 如果您仍想推送所有元素服务器端,只需从数组中自动完成;
  • 构建一个更复杂的自动完成模式,使用Web服务提供要显示的元素(考虑服务器端缓存,如下面的评论中的建议将是一个好主意)。

答案 2 :(得分:1)

您的主要问题是您每次都在扫描整个列表。这是你可以避免的事情,

一种方法是对列表进行排序。通过这样做,您可以确定何时到达比赛结束时搜索可以停止扫描。

您还应该执行二进制搜索,而不是扫描。这可以在排序列表上相对容易地完成。

如果失败,您应该创建对象的目录。类似的东西:

{
   aa: ['aardvark', 'aardwolf' ],
   ab: ['abstain'.....
   ...
}

这减少了真正必要的扫描量

答案 3 :(得分:1)

[编辑]有关示例,请参阅this jsfiddle,使用100.000元素的初始数组。

除了其他或多或少有用的建议:

首先优化是对数组进行排序。其次,您可以通过将其值转换为现成的Option对象来准备大型数组。第三,您是否考虑应用新的数组mapfilter方法?第四,使用RegEx test代替search。有了这些,您可以将过滤代码减少到:

//once, on page load
masterlist = masterlist
             .sort()
             .map(function(item){return new Option(item,item);});

//filtering
var optsfiltered = masterlist.filter(function(item){
     re.lastIndex = 0;
     return re.test(item.value);
    }).slice(0,maxToShow);

//replace selectBox options
selectBox.options.length = 0;
for (var i=0;i<optsfiltered.length;i++){
  selectBox[i] = optsfiltered[i];
}

对于浏览器兼容性,您可能希望使用mapfilter

的填充程序

答案 4 :(得分:0)

性能的最佳选择是在输入后加载数据。也就是说,当用户键入“tes”时,服务器会发回JSON字符串,如["test", ...]

下一个最好的选择就是像@Dancrumb建议的那样把事情分解成树。

如果你不能做其中任何一个,那么下一个最好的方法是确保你的数组有序并对起点进行二进制搜索。 Nicholas C. Zakas在这里有一个很好的例子:http://www.nczonline.net/blog/2009/09/01/computer-science-in-javascript-binary-search/

这会让你从O(N)转到O(lg(N))

您必须稍微修改它以搜索以您的值开头而不是等于您的值的字符串,然后向后搜索第一个实例。

<强>更新

刚看到你正在使用.search。这意味着您无法使用任何这些方法,并且无论是在服务器上进行昂贵的搜索还是在客户端上进行昂贵的搜索。请考虑检查它是否以值开头,而不是更快的搜索:How to check if a string "StartsWith" another string?

答案 5 :(得分:0)

以最快的方式遍历数组,以最快的方式执行for循环...

for(var z =listLength;--z;) { do what ever }

因为这样可以节省每个循环的评估(例如,a

如果我是你,我会使用不同的格式来保存它,以便快速搜索Trie

如果必须使用数组,至少将它们分成a,b,c等数组......

答案 6 :(得分:0)

好的,这是您遇到的最大问题以及为什么其他解决方案(包括我的其他解决方案)都不起作用:

if(masterList[i].search(re) != -1)

您正在对每个条目执行正则表达式搜索,直到您填充了列表为止!这不仅是一项代价高昂的操作,而且意味着您可能对此处的匹配感兴趣单词的中间,这意味着你不能依赖每个人提出的基于前缀的优化。

如果(A)你有很高的命中率,并且(B)你真的,真的想做search,那么这种方法将会运作得相当好:

  1. 当用户键入字母时,例如“a”,启动搜索。找到所有匹配的项目,例如5.将条目保存在缓存中。例如:

    myCache['a'] = {
        entries: ['aaaa', 'aaab', 'aaac', 'aaad', 'aaae'],
        lastIndex: 5
    };
    
  2. 当用户键入下一个字符,现在字符串为“ab”时,取字符串减一个字符,你就得到“a”,查看myCache["a"]并检查值,你有一个匹配“aaab”,但其他一切都失败了。你还需要4个。

  3. myCache["a"].lastIndex + 1开始。继续搜索,直到你有匹配。将结果存储在缓存中,您就得到了:

    myCache['ab'] = {
        entries: ['aaab', 'aaba', 'aabb', 'aabc', 'aabd'],
        lastIndex: 29
    };
    
  4. 随着字符串长度的增加,重复步骤2和3。