最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - JS Array.filter with Dynamic Filter Criterion - Stack Overflow

programmeradmin2浏览0评论

How can I dynamically declare a set of filters criteria without having to specify the number of filters?

For example, if I have a set of data, like this:

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

I Know that I can use JS .filter() to get all of the items that have type wood and size 8:

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

But what if I want to filter the items with an unknown number of filters, and have .filter() return all data items that match those criterion?

Here is a codepen that reflects the above code.

How can I dynamically declare a set of filters criteria without having to specify the number of filters?

For example, if I have a set of data, like this:

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

I Know that I can use JS .filter() to get all of the items that have type wood and size 8:

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

But what if I want to filter the items with an unknown number of filters, and have .filter() return all data items that match those criterion?

Here is a codepen that reflects the above code.

Share Improve this question edited Jul 1, 2015 at 20:12 Amit 46.3k9 gold badges82 silver badges113 bronze badges asked Jul 1, 2015 at 20:04 HimmelHimmel 3,7097 gold badges43 silver badges78 bronze badges 3
  • how would these dynamic filter criteria be passed to your code? – Rhumborl Commented Jul 1, 2015 at 20:06
  • I have a service Javascript file that accepts dynamic inputs and then returns the filtered data based on the input criterion. Is this what you wanted to know? – Himmel Commented Jul 1, 2015 at 20:07
  • i would write a simple comparator function and then chain my criteria in a series of filters, ex: r.filter(comp, ["type","wood"]).filter(comp, ["size",8); – dandavis Commented Jul 1, 2015 at 20:30
Add a comment  | 

4 Answers 4

Reset to default 14

You could pass an array of conditions to the filterItems() function. Try this:

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']]);

The same idea can be applied in various formats, such as using objects instead of an array for increased readability.

I just did some one line refactor on Amit's answer for any data structure and nested properties support

// 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' }])
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/

Amit's answer is great, but I wanted to add to it. In my case, I needed to return all parameters, else return none/false. Here is the edited code from Amit's

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']]);
发布评论

评论列表(0)

  1. 暂无评论