简体   繁体   中英

Filtering an array with multiple filter arrays

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.

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

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

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.

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); });

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))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM