4

http://jsfiddle.net/SqJ2y/

var list = [
    { mode: 1, type: 'foo', blah: 1 }, 
    { mode: 3, type: 'foo', blah: 1 }, 
    { mode: 3, type: 'foo', blah: 1 },
    { mode: 1, type: 'bar', blah: 1 }, 
    { mode: 2, type: 'bar', blah: 0 },
    { mode: 3, type: 'bar', blah: 1 }
];

var filter = [
    { propertyName: 'mode', value: 1 }, 
    { propertyName: 'type', value: 'foo' },
    { propertyName: 'blah', value: 1 }
];

var i = 0;

var result1 = $.grep(list, function(x){
    i++;
    return x.type === 'foo' && x.mode === 1 && x.blah === 1;
});

console.log(result1, 'iterations:', i); // 6 iterations

var j = 0;

var result2 = list;

$.each(filter, function(k, filter){
    result2 = $.grep(result2, function(listItem) {
        j++;
        return listItem[filter.propertyName] === filter.value;
    });
});

console.log(result2, 'iterations:', j); // 9 iterations

我想优化result2上面给出的过滤方法。

正如您在 中看到的result1,可以通过更少的迭代来获得相同的结果。在我的示例中,它可能看起来不多,但有很大的列表,其中性能是一个问题。

我的问题:有什么方法可以优化过滤result2,使其起到result1过滤的作用?

4

3 回答 3

2

您可以先构建一个匹配的对象,然后重新使用它来避免循环去循环:

var ob={};
filter.map(function(a,b){
  ob[a.propertyName]=a.value;
})

result2 =  $.grep(list, function(x){
   j++;
   return x.type === ob.tpye && x.mode === ob.mode && x.blah === ob.blah;
});


/* which has the console showing (and yes, they are the same two objects in both results): 
[Object] "iterations:" 6 
[Object] "iterations:" 6   */

完整:http: //jsfiddle.net/SqJ2y/2/

于 2013-07-04T10:04:11.777 回答
1

我的问题:有什么方法可以优化 result2 的过滤,使其可以作为 result1 过滤?

是的。grep正在为每个过滤器构建一个新数组,最好只做一次。使用本机filterevery方法:

var result3 = list.filter(function(listItem) {
    return filter.every(function(test) {
        return listItem[test.propertyName] === test.value;
    });
});

普通循环可能会更快。使用 jQuery 的迭代,它没有every等效项,无论如何你都会使用一个:

var result3 = $.grep(list, function(listItem) {
    for (var i=0, l=filter.length; i<l; i++)
        if (listItem[filter[i].propertyName] !== filter[i].value)
            return false;
    return true;
});

当然,这仍然需要filter每次都迭代 s ,你并没有真正解决这个问题(除非你编译 a new Function)。filter但是您可以通过将那些将过滤掉最多项目的属性放在前面来进一步优化数组。

编辑:这是一个编译函数的例子:

var result4 = list.filter(new Function("listItem",
    "return "+filter.map(function(test) {
         return "listItem."+test.propertyName+"==="+JSON.stringify(test.value);
// make sure that        ^ these are valid      ^  these are primitive
//                         identifiers             value only
    }).join(" && ")+";"
));

然而,它的性能需要彻底测试,因为评估函数体可能会引入巨大的开销,尤其是在较旧的 JS 引擎中。不过,对于数千个过滤行来说,它可能会更快。

于 2013-07-04T11:43:27.483 回答
0

您可以结合@dandavis 和@Bergi 之前的两个答案:

var filtered = list.filter(function(item) {
  return filter.every(function(f) {
    return f.value === item[f.propertyName];
  });
});
于 2013-07-04T15:38:36.423 回答