使用$ .grep()基于自定义过滤器过滤列表

时间:2013-07-03 09:53:43

标签: javascript jquery json

我想根据某些条件从列表中过滤掉某些对象:

var list = [
    { mode: 1, type: 'foo' }, 
    { mode: 2, type: 'foo' }, 
    { mode: 3, type: 'foo' }, // remove this item
    { mode: 1, type: 'bar' }, 
    { mode: 2, type: 'bar' },
    { mode: 3, type: 'bar' }
];

var blah = $.grep(list, function(x){

    if(x.type === 'foo')
        return x.mode !== 3;
    else
        return true;
});

console.log('result 1:', blah);

http://jsfiddle.net/65LeJ/

这同样有意义。

我的问题是我有一个过滤器,它从我的服务器发送为JSON:

var blah = list;

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

$.each(filter, function(k, filterRow){

    blah = $.grep(blah, function(listItem){
        return listItem[filterRow.propertyName] === filterRow.value;
    });

});

http://jsfiddle.net/65LeJ/1/

这显然会过滤掉,但它只是让你知道它是如何工作的。

我想知道的是关于过滤器应如何过滤出与第一个示例中相同数量的行的建议。

不,我不想传递表达式并使用eval()

如果有任何不清楚的地方,请告诉我。

6 个答案:

答案 0 :(得分:3)

您最好使用[].filter[].every

这是我的解决方案:

var list = [
    { mode: 1, type: 'foo' }, 
    { mode: 2, type: 'foo' }, 
    { mode: 3, type: 'foo' }, // remove this item
    { mode: 1, type: 'bar' }, 
    { mode: 2, type: 'bar' },
    { mode: 3, type: 'bar' }
];

var blah = list;

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

var filtered = list.filter(function(listItem) {
    return !filter.every(function(filterItem) {
        return listItem[filterItem.propertyName] === filterItem.value;
    });
});

console.log(filtered);

http://jsfiddle.net/Dogbert/65LeJ/2/

答案 1 :(得分:2)

如果你想保留$ .each !!!

var list = [
    { mode: 1, type: 'foo' }, 
    { mode: 2, type: 'foo' }, 
    { mode: 3, type: 'foo' }, // remove this item
    { mode: 1, type: 'bar' }, 
    { mode: 2, type: 'bar' },
    { mode: 3, type: 'bar' }
];

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

var blah = $.grep(list, function(x){
    var result = true;    

    $.each(filter, function(index, element) {
        result &= (x[element.propertyName] === element.value)
    });

    return !result;
});

console.log('result 1:', blah);

答案 2 :(得分:1)

var list = [
    { mode: 1, type: 'foo' }, 
    { mode: 2, type: 'foo' }, 
    { mode: 3, type: 'foo' }, 
    { mode: 1, type: 'bar' }, 
    { mode: 2, type: 'bar', name: 'Agnes'}, // remove this item
    { mode: 3}
];

var filter = [
    { propertyName: 'mode', value: 2 }, 
    { propertyName: 'type', value: 'bar' },
    { propertyName: 'name', value: 'Agnes' }
];

var filter_obj = {};
var i;

for(i=0; i<filter.length; ++i) {
  var obj = filter[i];
  filter_obj[obj.propertyName] = obj.value;
}

var results = [];
var filter_len = Object.keys(filter_obj).length;

NEXT_OBJECT:
for(i=0; i<list.length; ++i) {
  obj = list[i];

  if(Object.keys(obj).length != filter_len) {
    results.push(obj);
  }
  else {
    for(key in obj) {
      if(obj[key] != filter_obj[key]) {
        results.push(obj);
        continue NEXT_OBJECT;
      }
    }
  }
}

console.log(results);

--output:--
[Object { mode=1, type="foo"}, 
 Object { mode=2, type="foo"}, 
 Object { mode=3, type="foo"}, 
 Object { mode=1, type="bar"}, 
 Object { mode=3}]

答案 3 :(得分:1)

另一种方法,如果你不介意使用Function构造函数与Crockford的“Eval is Evil”相冲突,就像这样:

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

var makeGrepFn = function(filter) {
    return new Function("listItem", (function() {
        var body = [], idx = -1;
        while (++idx < filter.length) {
            body.push("if ('' + listItem['" + 
                    filter[idx].propertyName + "'] !== '" + 
                    filter[idx].value + "') {return true;}");
        }
        body.push("return false;");
        return "    " + body.join("\n    ");
    }()));
};

var grepFn = makeGrepFn(filter);

var filtered = $.grep(list, grepFn);

这有各种各样的限制。它将字符串和数字进行比较。虽然可以修复,但会使代码复杂化。它不适用于更复杂的对象,只有具有原始属性的对象,但它仍然是一种有趣的方法。

grepFn最终将成为与此相当的东西:

function (listItem) {
    if ('' + listItem['mode'] !== '3') {return true;}
    if ('' + listItem['type'] !== 'foo') {return true;}
    return false;
}

如果您愿意,显然您可以使用Array.prototype.filter代替jQuery.grep来执行此类操作。您只需撤消上面的truefalse

答案 4 :(得分:1)

正如Dogbert所说,filterevery完成了这项工作。但是,这是一种非常慢的方法。这是SQL查询有用的东西。考虑在SQL中有以下表:

      List
+------+------+
| Mode | Type |
+------+------+
|    1 |  foo |
+------+------+
|    2 |  foo |
+------+------+
|    3 |  foo |
+------+------+
|    1 |  bar |
+------+------+
|    2 |  bar |
+------+------+
|    3 |  bar |
+------+------+

您可以执行以下两个查询之一来获取所需的数据:

查询1:构造性(创建新表)

SELECT * FROM List WHERE Mode != '3' AND Type != 'foo';

查询2:具有破坏性(从表中删除行)

DELETE FROM List WHERE Mode = '3' AND Type = 'foo';

当然,JavaScript不是SQL。但是,我们可以使用这两个SQL查询作为指导,在JavaScript中创建两个生成正确结果的函数。

功能1:具有建设性

function filter(table, conditions) {
    var rows = table.length, length = conditions.length, result = [];

    for (var i = 0; i < rows;) {
        var row = table[i++];

        for (var j = 0; j < length;) {
            var condition = conditions[j++];

            if (row[condition.propertyName] !== condition.value) {
                result.push(row);
                break;
            }
        }
    }

    return result;
}

功能2:破坏性

function remove(table, conditions) {
    var rows = table.length, length = conditions.length, result = [];

    loop: for (var i = 0; i < rows;) {
        var row = table[i++];

        for (var j = 0; j < length;) {
            var condition = conditions[j++];
            if (row[condition.propertyName] !== condition.value)
                continue loop;
        }

        table.splice(--i, 1);
        rows--;
    }
}

现在给出一个表格和条件列表,您可以filterremove您不想要的行。

考虑您有下表:

var table = [
    { mode: 1, type: 'foo' }, 
    { mode: 2, type: 'foo' }, 
    { mode: 3, type: 'foo' }, // remove this item
    { mode: 1, type: 'bar' }, 
    { mode: 2, type: 'bar' },
    { mode: 3, type: 'bar' }
];

考虑到以下条件:

var conditions = [
    { propertyName: 'mode', value: 3 }, 
    { propertyName: 'type', value: 'foo' }
];

你可以这样做:

var result = filter(table, conditions);

或者你可以这样做:

remove(table, conditions);

我个人更喜欢filter而不是remove(以防您需要原始表格)。


好的,那么这些方法如何比Dogbert的解决方案更好?答案是因为它们更快。它们如此之快的原因是:

  1. 您不使用[].filter,因为您对表格中的每一行都执行了回调,因此速度很慢。
  2. 您不使用[].every,因为您为每个条件执行回调,所以filter也很慢。
  3. 本质上,remove和{{1}}都是Dogbert的解决方案。但是,由于它们是手工制作而不使用回调,因此执行速度要快得多。自己看结果:

    http://jsperf.com/filter-and-every-vs-filter-vs-remove

答案 5 :(得分:1)

如果无法使用Array.filter之类的.every方法,但拥有jQuery,那么您可以试试这个:

var filtered = $.grep(list, function(el) {
  return $.grep(filter, function(filterEl) {
    return el[filterEl.propertyName] === filterEl.value;
  }).length !== 2;
});

这是一个非常具体的问题解决方案,但它比其他答案更容易推理。