admin管理员组文章数量:1323714
I have this project which has a search page where I have a basic filter form. I managed to filter the data but only with one filter at a time. I can't figure the logic behind applying multiple filters in order to narrow down the data.
Example:
let data = [{
budget: "220",
categories: ["party", "school"]
},
{
budget: "450",
categories: ["self"]
},
{
budget: "600",
categories: ["dev", "work"]
}
];
const filters = {
budget: ["200","500"],
categories: ["party"]
}
//////Expected behavior:
Outputs only the first object because it's budget it's between 200 and 500 and it has "party" as it's category.
This example simulates basically what I have in my app. As you can see, I have an array of objects. Each object has a budget and multiple categories. For the filters, let's say I apply a budget filter (which is going to be a range filter) and one category filter. How should I chain this filters together in order to filter the data properly?
I have this project which has a search page where I have a basic filter form. I managed to filter the data but only with one filter at a time. I can't figure the logic behind applying multiple filters in order to narrow down the data.
Example:
let data = [{
budget: "220",
categories: ["party", "school"]
},
{
budget: "450",
categories: ["self"]
},
{
budget: "600",
categories: ["dev", "work"]
}
];
const filters = {
budget: ["200","500"],
categories: ["party"]
}
//////Expected behavior:
Outputs only the first object because it's budget it's between 200 and 500 and it has "party" as it's category.
This example simulates basically what I have in my app. As you can see, I have an array of objects. Each object has a budget and multiple categories. For the filters, let's say I apply a budget filter (which is going to be a range filter) and one category filter. How should I chain this filters together in order to filter the data properly?
Share Improve this question edited Oct 5, 2018 at 14:50 meta4 7361 gold badge11 silver badges25 bronze badges asked Oct 5, 2018 at 14:07 Ovidiu GOvidiu G 1,2735 gold badges25 silver badges48 bronze badges4 Answers
Reset to default 5Each filter should have a function that can handle the check. The filterHandlers
is a Map of such handlers.
The array and the object of filters are passed to applyFilters()
. The method gets the keys of the filters (via Object.keys()
), and iterates the items with Array.filters()
. Using Array.every()
the item is checked by all filters (using the relevant handler). If all checks pass, the item will be included in the resulting array.
Whenever you need to add another filter, you add another handler to the Map.
const filterHandlers = new Map([
[
'budget',
(val, [min, max]) => val >= min && val <= max
],
[
'categories',
(current, categories) => current.some( // some - at least one, every - all of them
(c) => categories.includes(c)
)
],
[]
]);
const applyFilters = (arr, filters) => {
const filterKeys = Object.keys(filters);
return arr.filter(o => filterKeys.every((key) => {
const handler = filterHandlers.get(key);
return !handler || handler(o[key], filters[key]);
}));
}
const data = [{"budget":"220","categories":["party","school"]},{"budget":"450","categories":["self"]},{"budget":"600","categories":["dev","work"]}];
const filters = {"budget":["200","500"],"categories":["party"]};
const result = applyFilters(data, filters);
console.log(result);
This question is a great excuse for a functional programming excercise :D
Array.prototype.filter
takes a predicate function. A predicate takes one argument and returns a boolean. Although javascript won't plain, it makes sense that the types of the elements in the array match the types your predicate can handle.
One predicate
You already got to the point of filtering with one predicate, but I'll include an example anyway:
// [ number ]
const numbers = [ 1, 2, 3, 4, 5, 6 ];
// number -> bool
const lt5 = x => x < 5;
// [ number ] -> (number -> bool) -> [ number ]
const result = numbers.filter(lt5);
console.log(result); // [ 1, 2, 3, 4 ]
Two predicates
Now, let's say you only want the even numbers that are less than 5... How do we apply multiple filters? The most straightforward way is to filter twice:
// [ number ]
const numbers = [ 1, 2, 3, 4, 5, 6 ];
// number -> bool
const lt5 = x => x < 5;
// number -> bool
const even = x => x % 2 === 0;
const result = numbers
.filter(lt5) // [ 1, 2, 3, 4 ]
.filter(even);
console.log(result); // [ 2, 4 ]
Any or All predicates?
Although some people will plain about efficiency (this loops 10 times), I'd actually remend this approach whenever you need the elements to pass all of multiple filters.
However, if we want to switch between being able to filter items that either all, or any of our predicates, we need another approach. Luckily, there's some
and every
!
// [ number ]
const numbers = [ 1, 2, 3, 4, 5, 6 ];
// number -> bool
const lt5 = x => x < 5;
// number -> bool
const even = x => x % 2 === 0;
// number -> bool
const lt5_OR_even = x => [lt5, even].some(f => f(x));
// number -> bool
const lt5_AND_even = x => [lt5, even].every(f => f(x));
console.log(
numbers.filter(lt5_OR_even) // [ 1, 2, 3, 4, 6 ]
);
console.log(
numbers.filter(lt5_AND_even) // [ 2, 4 ]
);
Composing predicates
Instead of looping over arrays of predicates, we can also take a different approach. We can pose our predicates in to new ones using two small helpers, both
and either
:
// (a -> bool) -> (a -> bool) -> a -> bool
const both = (f, g) => x => f(x) && g(x);
// (a -> bool) -> (a -> bool) -> a -> bool
const either = (f, g) => x => f(x) || g(x);
const numbers = [ 1, 2, 3, 4, 5, 6 ];
const lt5 = x => x < 5;
const even = x => x % 2 === 0;
console.log(
numbers.filter(either(lt5, even)) // [ 1, 2, 3, 4, 6 ]
);
console.log(
numbers.filter(both(lt5, even)) // [ 2, 4 ]
);
With these helpers, we can take any array of predicates and merge them in to one! The only thing we need to add is a "seed" so we can reduce
safely:
// (a -> bool) -> (a -> bool) -> a -> bool
const both = (f, g) => x => f(x) && g(x);
// (a -> bool) -> (a -> bool) -> a -> bool
const either = (f, g) => x => f(x) || g(x);
// any -> bool
const True = _ => true;
const Filter = (predicates, parer = both) =>
predicates.reduce(parer, True);
const myPred = Filter([
x => x > 5,
x => x < 10,
x => x % 2 === 0
]);
console.log(
[1,2,3,4,5,6,7,8,9,10,11].filter(myPred) // [ 6, 8 ]
);
Back to your data!
Putting it all together, you start to realize this overplicates things for simple examples
本文标签: javascriptApplying multiple filters on an arrayStack Overflow
版权声明:本文标题:javascript - Applying multiple filters on an array - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742127902a2422025.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论