JS数组.filter处理动态过滤条件

4

我如何动态声明一组过滤条件,而不必指定过滤器的数量?

例如,如果我有以下数据集:

var data = [ 
  { item: { type: 'wood', size: 10 } }, 
  { item: { type: 'wood', size: 8 } },
  { item: { type: 'metal', size: 8 } } 
]

我知道我可以使用JS .filter() 方法来获取所有类型为 wood 和尺寸为 8 的项目:

function filterItems() {
  return data.filter(function(val) {
    return val['item'].type == 'wood' && 
           val['item'].size == 8;
  }
}

如果我想使用未知数量的筛选器来过滤项目,并让.filter()返回所有符合这些条件的data项目,该怎么办?这里是一个反映上述代码的codepen

这些动态筛选条件会如何传递给您的代码? - Rhumborl
我有一个服务JavaScript文件,它接受动态输入,并根据输入条件返回过滤后的数据。这是你想知道的吗? - Himmel
我会编写一个简单的比较函数,然后将我的条件链接在一系列过滤器中,例如:r.filter(comp, ["type","wood"]).filter(comp, ["size",8]); - dandavis
4个回答

14

您可以将条件数组传递给filterItems()函数。请尝试:

function filterItems(filters) {
  return data.filter(function(val) {
    for(var i = 0; i < filters.length; i++)
      if(val['item'][filters[i][0]] != filters[i][1])
        return false;
    return true;
  }
}
filterItems([['type', 'wood'], ['size', 8], ['someother', 'value']]);

相同的想法可以应用于各种格式,例如使用对象代替数组以增加可读性。


我觉得我理解你想要展示的东西了,能请你整理一下你的代码吗? - Himmel
你需要进行哪种清理? - Amit
那么...可以吗?还是不行?我不明白(如果不行,请具体说明,你不喜欢什么?) - Amit
这回答了我的问题。这是过滤输入范围的好方法,比如 size >= 5 吗?有没有一种方法可以在 filterItems() 中不必包含额外的 if 语句来实现这一点? - Himmel
如果您需要更详细的条件,可以通过一个(更复杂的)内部if引入这种逻辑(可能在数组中有更多的值),或者回到major-mann答案中的方法并使用函数作为条件。考虑到您关于条件来自何处的评论,似乎两种方法的复杂性都是相同的。 - Amit
显示剩余3条评论

3

我刚刚对Amit的答案进行了一行重构,以支持任何数据结构和嵌套属性

// the function
filterItems = (data, filters) => data.filter(item => !filters.find(x => x.key.split('.').reduce((keys, key) => keys[key], item) !== x.value))

// how to use it
filterItems(data, [{ key: 'type', value: 'wood' }, { key: 'some.nested.prop', value: 'value' }])

1
"Amit的回答很好,但我想补充一下。在我的情况下,我需要返回所有参数,否则返回none/false。这是从Amit的编辑后的代码:"
function filterItems(filters) {
  return data.filter(function(val) {
    let result = true;
    for(var i = 0; i < filters.length; i++)
      if(val['item'][filters[i][0]] != filters[i][1])
        result = false;
    return result;
  }
}
filterItems([['type', 'wood'], ['size', 8], ['someother', 'value']]);

1
function isSingle(filter) {
    return (filter && 'o' in filter && 'm' in filter && 'v' in filter);
}

function isComposite(filter) {
    return (filter && 'lo' in filter);
}

function createBody(filter) {

    if (isComposite(filter)) {
        var bdy = "";
        if (filter.v.length > 1) {
            var o = filter.lo;
            return "(" + createBody(filter.v.shift()) + " " + o + " " + createBody({ lo: filter.lo, v: filter.v }) + ")";
        } else if (filter.v.length == 1) {
            return createBody(filter.v.shift());
        }
        return bdy;
    } else if (isSingle(filter)) {
        var o = filter.o;
        if (typeof filter.v == "string") filter.v = "'" + filter.v + "'"
        return "item." + filter.m + " " + o + "  " + filter.v;
    }
}
var createFunc = function (filter) {

    var body = createBody(filter);
    var f = new Function("item", " return " + body + ";");
    return f;
}

function applyFilter(input, filter) {
    if (filter == undefined) {
        return input;
    }

    var fun = createFunc(filter);
    var output = input.filter(fun);
    return output;
};
//m:member,o:operator,v:value.

var filterQuery1 = { m: "item.type", o: "==", v: "metal" };//simpe query
var filterQuery2 = { m: "item.size", o: ">", v: 8 };
var filterQuery3 = {
    lo: "&&", v: [
        { m: "item.type", o: "==", v: "metal" },
        { m: "item.size", o: "<", v: 9 }]
}; //composite query
var data = [
  { item: { type: 'wood', size: 10 } },
  { item: { type: 'wood', size: 8 } },
  { item: { type: 'metal', size: 8 } }
]
var result = applyFilter(data, filterQuery1);// or filterQuery2,filterQuery3

console.log(result);

https://jsfiddle.net/kd0kL098/


3
请用简洁明了的语言总结你的答案。 - Christian Gollhardt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接