Reputation: 66
I'm trying to filter an array of items based on multiple filter arrays. Each of the items has a selection of filters, and I need to show only the ones that match all of the selected filters.
const selectedFilters = {
color: ["Red", "Blue"],
type: ["Shirt"],
size: ["M"]
};
const items = [
{
name: "Item 1",
filters: {
color: ["Red", "Blue", "Black"],
type: ["Shirt"],
size: ["M"]
}
},
{
name: "Item 2",
filters: {
color: ["Red"],
type: ["Pants"],
size: ["M"]
}
}
];
This is how I've been trying to solve it. Filter through all of the items - for each filter category that is not empty, go through all the filter words and check that the item matches all of them.
const filterItems = (items, selectedFilters) => {
const filterKeys = Object.keys(selectedFilters);
return items.filter(item => {
return filterKeys.every(key => {
// Ignore empty filters
if (!selectedFilters[key].length) {
return true;
}
return selectedFilters[key].every(filterWord => {
item.filters[key].includes(filterWord);
});
});
});
};
filterItems(items, selectedFilters);
Returns an empty array, should return an array with the "Item 1" object.
Upvotes: 0
Views: 2690
Reputation: 159
getFilteredResult = (data)=>{
let res = true;
for(d in data) {
if(d == 'filters') {
let obj = data[d];
for(key in obj) {
for(filterkey in selectedFilters) {
if(filterkey == key) {
selectedFilters[filterkey].forEach(data=>{
if(!obj[key].includes(data)){
res=false;
}
})
}
}
}
}
}
return res;
}
items.filter((data,index,arr)=> getFilteredResult(data))
Upvotes: 0
Reputation: 8885
There are many ways to solve this. For example, your solution is right but we need remove the curly brakets {}
inside the every callback like this:
filterWord => item.filters[key].includes(filterWord)
Another option could be converting the selectedFilters
as an filterValues
array, using the flat()
method. This method creates a new array with all sub-array elements concatenated into it recursively. Now, we extract the filters
of each items
and convert it as itemFilters
array using the flat()
method again. Then we try to match them with the filterValues
combining every()
and includes()
methods. Both will test whether all elements in the array include the elements of each filter
.
The third option is very similar to your solution. In this case, we extracts the selectedFilters
keys into a types
array. Then for every type
we call the some()
method combined with includes()
method to test whether at least one element in the array is included in the items.filters[type]
.
Here you have the implementation:
const selectedFilters = {
color: ['Red', 'Blue'],
type: ['Shirt'],
size: ['M']
};
const items = [
{
name: 'Item 1',
filters: {
color: ['Red', 'Blue', 'Black'],
type: ['Shirt'],
size: ['M']
}
},
{
name: 'Item 2',
filters: {
color: ['Red'],
type: ['Pants'],
size: ['M']
}
},
{
name: 'Item 3',
filters: {
color: ['Green', 'Blue', 'Red'],
type: ['Shirt'],
size: ['S']
}
},
{
name: 'Item 4',
filters: {
color: ['Red', 'Blue'],
type: ['Shirt'],
size: ['M']
}
},
];
const filterItems = (items, selectedFilters) => {
const filterKeys = Object.keys(selectedFilters);
return items.filter(item => {
return filterKeys.every(key => {
// Ignore empty filters
if (!selectedFilters[key].length) {
return true;
}
return selectedFilters[key].every(filterWord => item.filters[key].includes(filterWord));
});
});
};
const results = filterItems(items, selectedFilters);
console.log('Your option: ');
results.forEach(function (item, key) {
console.log('Name: ', item.name);
});
const filterValues = Object.values(selectedFilters).flat();
const results2 = items.filter(({filters}) => {
const itemFilters = Object.values(filters).flat();
return filterValues.every(filter => itemFilters.includes(filter));
})
console.log('Second option: ');
results2.forEach(function (item, key) {
console.log('Name: ', item.name);
});
const types = Object.keys(selectedFilters);
const results3 = items.filter(
item => types.every(
type => selectedFilters[type].some(
filter => item.filters[type].includes(filter)
)
)
);
console.log('Third option: ');
results3.forEach(function (item, key) {
console.log('Name: ', item.name);
});
Upvotes: 0
Reputation: 3562
You can use the Array.filter() function for this. Here is the very simple and effective solution fo this.
Array.prototype.diff = function(arr2) {
var ret = [];
for(var i in this) {
if(arr2.indexOf(this[i]) > -1){
ret.push(this[i]);
}
}
return ret;
};
var filteredArr = items.filter(function (el) {
return el.filters.color.diff(selectedFilters.color).length > 0 &&
el.filters.type.diff(selectedFilters.type).length > 0 &&
el.filters.size.diff(selectedFilters.size).length > 0
});
Here is the working example..
https://playcode.io/325371?tabs=console&script.js&output
Note: you can change AND / OR condition as per your requirements.
Upvotes: 0
Reputation: 26844
You can use some
to check at least one element is included in the array and the function expects a bool return value.
The
some()
method tests whether at least one element in the array passes the test implemented by the provided function. It returns a Boolean value.
const selectedFilters = {"color":["Red","Blue"],"type":["Shirt"],"size":["M"]}
const items = [{"name":"Item 1","filters":{"color":["Red","Blue","Black"],"type":["Shirt"],"size":["M"]}},{"name":"Item 2","filters":{"color":["Red"],"type":["Pants"],"size":["M"]}}]
const filterItems = (items, selectedFilters) => {
const filterKeys = Object.keys(selectedFilters);
return items.filter(item => {
return filterKeys.every(key => {
return selectedFilters[key].some(filterWord => { //Use some
return item.filters[key].includes(filterWord); //Return the a bool
});
})
});
};
var result = filterItems(items, selectedFilters);
console.log(result);
Shorter version:
const selectedFilters = {"color":["Red","Blue"],"type":["Shirt"],"size":["M"]};
const items = [{"name":"Item 1","filters":{"color":["Red","Blue","Black"],"type":["Shirt"],"size":["M"]}},{"name":"Item 2","filters":{"color":["Red"],"type":["Pants"],"size":["M"]}}];
const filterItems = (items, selectedFilters) => {
const fkeys = Object.keys(selectedFilters);
return items.filter(i => fkeys.every(k => selectedFilters[k].some(o => i.filters[k].includes(o))));
};
const result = filterItems(items, selectedFilters);
console.log(result);
Upvotes: 0
Reputation: 7891
You can create the array of values of selectedFilters
as well array of filters
property values. Then use every
on selectedFilters
to check if all values in it are present within the filters
.
const selectedFilters = {
color: ["Red", "Blue"],
type: ["Shirt"],
size: ["M"]
};
const items = [
{
name: "Item 1",
filters: {
color: ["Red", "Blue", "Black"],
type: ["Shirt"],
size: ["M"]
}
},
{
name: "Item 2",
filters: {
color: ["Red"],
type: ["Pants"],
size: ["M"]
}
}
];
const filterArr = Object.values(selectedFilters).flat();
const output = items.filter(({filters}) => {
const objFilters = Object.values(filters).flat();
return filterArr.every(val => objFilters.includes(val));
})
console.log(output);
Upvotes: 1