使用jQuery提高搜索JSON对象的性能

时间:2012-03-29 19:35:03

标签: javascript jquery json

如果已经在某个地方回答了这个问题,请原谅我。我搜索过,似乎这是一个相当具体的案例。

以下是JSON的示例(注意:这是非常剥离 - 这是动态加载的,目前有126条记录):

var layout = {
"2":[{"id":"40","attribute_id":"2","option_id":null,"design_attribute_id":"4","design_option_id":"131","width":"10","height":"10",
            "repeat":"0","top":"0","left":"0","bottom":"0","right":"0","use_right":"0","use_bottom":"0","apply_to_options":"0"},
    {"id":"41","attribute_id":"2","option_id":"115","design_attribute_id":"4","design_option_id":"131","width":"2","height":"1",
            "repeat":"0","top":"0","left":"0","bottom":"4","right":"2","use_right":"0","use_bottom":"0","apply_to_options":"0"},
    {"id":"44","attribute_id":"2","option_id":"118","design_attribute_id":"4","design_option_id":"131","width":"10","height":"10",
            "repeat":"0","top":"0","left":"0","bottom":"0","right":"0","use_right":"0","use_bottom":"0","apply_to_options":"0"}],
"5":[{"id":"326","attribute_id":"5","option_id":null,"design_attribute_id":"4","design_option_id":"154","width":"5","height":"5",
            "repeat":"0","top":"0","left":"0","bottom":"0","right":"0","use_right":"0","use_bottom":"0","apply_to_options":"0"}]
};

我需要匹配正确的值组合。这是我目前使用的功能:

function drawOption(attid, optid) {
    var attlayout = layout[attid];
    $.each(attlayout, function(k, v) {
        // d_opt_id and d_opt_id are global scoped variable set elsewhere
        if (v.design_attribute_id == d_att_id
            && v.design_option_id == d_opt_id  
            && v.attribute_id == attid 
            && ((v.apply_to_options == 1 || (v.option_id === optid)))) {
                // Do stuff here
        }
    });
}

问题是我可能会迭代10-15个布局(独特的attid),任何给定的布局(attid)可能有多达50种可能性,这意味着这个循环正在运行很多。

考虑到必须匹配的多个标准,AJAX调用会更好吗? (这个JSON是通过PHP动态创建的,所以我可以创建一个可以更有效地执行此操作的PHP函数), 或者我完全错过了有关如何在JSON对象中查找项目的内容?

与往常一样,欢迎任何改进代码的建议!

修改
我为没有说清楚而道歉,但这个问题的目的是找到一种方法来改善性能。这个页面有很多javascript,这是我知道性能低于它的位置。

9 个答案:

答案 0 :(得分:5)

首先,您应该衡量,并且只有在存在真正的性能问题时才会采取行动。您需要200毫秒的确切数字或80%的时间。 "它运行很多"什么都没有意义。浏览器可以非常快地循环。

您可以像其他人提到的那样改进常数因子,例如使用原生for循环而不是 jQuery.each 。在这种情况下,追逐全局变量对你的帮助太大了。

如果你真的想提高效率,你应该找到比O(n)更好的算法。假设您只使用此数据来查找符合特定条件的元素,您可以使用JS对象作为哈希来实现O(1)性能。

仅举例说明具体案例:

var layout = {
  "2": { "2,,4,131,10,0": ["40", "93"], "2,115,4,131,0": ["41"] },
  "4": { ... },
  ...
};

在php中生成此输出相当容易,然后您只需使用查找来查找符合您特定条件的ID。

答案 1 :(得分:2)

我的第一个建议是停止使用$.each,如果你想要尽可能地挤出一点性能。 jQuery.each比传统循环做得更多。在浏览器的调试器运行时查看this jsFiddle(即Safari / Chrome的Web开发人员工具)并逐步完成,直到执行从jQuery完全返回。

作为参考,小提琴的代码是:

var myArr = [{"foo":"bar"},{"answer":42}];

debugger;

$.each(myArr, function(k, v) {
    console.log(k + ': ');
    console.dir(v);
});​

现在浏览the second version

var myArr = [{"foo":"bar"},{"answer":42}],
    i, j;

debugger;

for (i = 0, j = myArr.length; i < j; i += 1) {
    console.log('myArr[' + i + ']: ');
    console.dir(myArr[i]);
}​

请注意,第二个版本中执行的操作要少得多。所以这是微小位的性能提升。

其次,尽可能多地消除本地范围之外的查找次数。即使你缓存了对全局变量的引用(boo!),你也可以save a lot of time,因为这个循环可能会被执行数百次。所以而不是:

function foo(a, b) {
    if (a === globalA && b === globalB) {
        // Do stuff
    }
}

你会这样做:

function foo(a, b) {
    var gA = globalA,
        gB = globalB;
    if (a === gA && b === gB) {
        // Do stuff
    }
}

至于根据对象成员配对条件,我没有看到其他可以改进的东西。您正在检查的对象属性是顶级的,您正在查看每个对象的本地实例(因此范围链查找很短)。

如果不了解这实际上应该如何运作,那么这些是我能提出的最佳建议。但是,我可以猜测,您从简单的JSON数据开始的想法将是一个很大的改进。如果您知道布局及其约束是什么,那么从服务器请求特定细节将意味着您不必检查这么多条件。您可以简单地向服务器询问您实际需要实现的细节并循环遍历(更新DOM或其​​他)。

答案 2 :(得分:2)

恕我直言,一个简单的hashmap索引可能效果最好。这确实需要您提前循环数据,但索引可以很容易地附加到和缓存。

生成索引后,对于查找应该是O(1),并且每个键将处理多个条目。

    var layout = {
    "2":[[...], ...],
    "5":[[...], ...]
    };

    var index = {};

    function makeKey(data) {
        return data.join('_');
    }

    for(var l in layout) {
        var cur = layout[l];

        for(var i in cur) {
            var item = cur[i];
            var key = makeKey([item.p1, item.p2, ...]);

            index[key] = index[key] || [];
            index[key].push(item);
        }
    }

    function find(attid, optid) {
        var key = makeKey([attid, optid, 1, d_att_id, ...]);
        return index[key]; //this is an array of results
    }

答案 3 :(得分:1)

我看到你在搜索 5个字段:v.design_attribute_id,v.design_option_id,v.attribute_id,v.apply_to_options,v.option_id。

你可以做的是在名为“key”的对象中添加一个额外的字段,该字段是这些字段中值的组合。

这是一个例子

       {
            "key": "4_131_2_0_0" //i picked 0 to represent null, but you can pick any number                                    
            "id": "40",
            "attribute_id": "2",
            "option_id": null,
            "design_attribute_id": "4",
            "design_option_id": "131",
            "width": "10",
            "height": "10",
            "repeat": "0",
            "top": "0",
            "left": "0",
            "bottom": "0",
            "right": "0",
            "use_right": "0",
            "use_bottom": "0",
            "apply_to_options": "0"
        }

请注意,您必须规范化每个值的长度。 这意味着如果一个对象optionId为1而另一个对象optionID为566则必须表示 密钥字符串中的第一个选项ID为001。

使用此字段,您可以在将数组返回到客户端之前对服务器端的数组进行排序。

然后,您可以使用二进制搜索在客户端上查找值。

使用此处的二进制搜索实现 http://www.nczonline.net/blog/2009/09/01/computer-science-in-javascript-binary-search/

您的搜索功能类似于

function drawOption(attid, optid) {
    var attlayout = layout[attid];
    var needle = d_att_id + "_" + d_opt_id + "_" + attid + "_" + optid; //remember to normalize  length if you have to

    var idx = binarySearch(attlayout,needle);

    var item;
    if(idx !== -1){
        item = attlayout[idx];
        //do something 

    }
}

您可以尝试使用此复合键构思的另一种方法是让服务器返回 映射的一个大对象中的布局对象 attid,v.design_attribute_id,v.design_option_id,v.attribute_id,v.apply_to_options,v.option_id

然后你可以在O(1)时间内查找。 它看起来像

 function drawOption(attid, optid) {

    var needle = attid + "_" + d_att_id + "_" + d_opt_id + "_" + attid + "_" + optid; //remember to normalize  length if you have to

    var item = layout[needle];
    if(typeof item !== "undefined"){
        //do something 

    }
}

答案 4 :(得分:0)

在尝试改进代码时,最好使用firebug分析来检查哪些函数需要时间。您可以通过单击firebug控制台面板中的配置文件按钮进行配置,然后运行代码或使用代码中的firebug's profiling commands

根据您提供的代码,只能给出一些改进点。

  1. $。与原生循环解决方案相比,每个都很慢。为了最好 循环解决方案,请查看此JsPref test
  2. 最好将JSON更改为使用数组而不是对象文字。据说检索值会更快。

答案 5 :(得分:0)

我之前遇到过这样的问题,我的js对象数组包含8千条记录以及更多。 我的经验不是关于性能,而是关于代码的可读性,可维护性和可扩展性。

因此我在2年前开发了一个JS对象查询库:JSOQL http://code.google.com/p/jsoql/

它像SQL一样,允许您使用类似于SQL的语法查询对象的js数组。

示例用法是这样的,我真的不记得了,但您可以在下载选项卡中下载示例用法。

new JSQOL().Select([field1, field2, field3 ...]).From({ ... }) .Where(fn) .Offset(int) .Limit(int) .Get();

注意: {...}是你的对象数组,或者是它自己的对象。

希望有所帮助,如果您需要更多信息,可以给我发消息。

答案 6 :(得分:0)

它无处不在,但你的问题听起来好像可以用webworkers来完成

如果你没有网络工作者,我会看到的另一件事是试图不阻止ui长。如果您可以将其分成大约40ms的位,然后在几毫秒之后设置下一个chunck,那么用户将获得更愉快的体验。这需要一点点摆弄,但是当事情需要超过50到100毫秒之间时,用户会开始注意到东西

答案 7 :(得分:-1)

您是否考虑过使用jQuery grep函数?

jQuery grep

jquery grep on json object array为例。

答案 8 :(得分:-1)

这种技术可能会以更多的内存为代价,从而产生更好的性能。

我只是为了说明这个概念,我的代码示例很简单。

首先,您希望将JSON数据预处理到一些充当索引的其他数组中。以下是预处理后最终数组的外观示例:

var layouts_by_attribute = {
    // attribute_id => array(layouts)
    2: [40, 41, 44],
    5: [326]
};

var layouts_by_design_attribute_id = {
    // design_attribute_id => array(layouts)
    4: [40, 41, 44, 326]
};

现在可以非常快速地按属性查找布局:

function findByAttribute(attribute_id) {
    return layouts = layouts_by_attribute[attribute_id];
}

function findByDesignAttribute(design_attribute_id) {
    return layouts = layouts_by_design_attribute[design_attribute_id];
}