Home > Software design >  Filter array by object property values
Filter array by object property values

Time:08-19

I am programming a function that will handle javascript array filtering. I know the values by which I want to filter so I know how to do it in a fairly easy way, but I would like the code to be more extensible.

I wrote such a function:

 const items = [
{
    prop1: 'Jotahan',
    prop2: 'London',
    prop3: '1234567'
},
{
    prop1: 'Jones',
    prop2: 'Paris',
    prop3: '987'
}
];

const filters = { prop2: 'Paris', prop3: '987' };

   const handleFilters = (items, filters) => {
        return items.filter((item) => {
            if (filters.prop3 && filters.prop2) {
                return item.prop3 === filters.prop3 && item.prop2 === filters.prop2;
            }

            if (filters.prop3) {
                return item.prop3 === filters.prop3;
            }

            if (filters.prop2) {
                return item.prop2 === filters.prop2;
            }
        });
    }

I am not completely satisfied with it. I think it could be written better. If the 3rd argument comes, I don't want to add it to the if - it should be automatic.

I've searched several topics on stackoverflow, looked through the lodash documentation looking for some good solution but I have no idea what I can do better with this.

CodePudding user response:

If I understood correctly, you want to only keep the items that match all the filter:

const items = [
    {
        prop1: 'Jotahan',
        prop2: 'London',
        prop3: '1234567'
    },
    {
        prop1: 'Jones',
        prop2: 'Paris',
        prop3: '987'
    }
];

const filters = {prop2: 'Paris', prop3: '987'};

const handleFilters = (items, filters) => {
    return items.filter((item) =>
        Object.entries(filters).every(([key, val]) => item[key] === val)
    )
};

console.log(handleFilters(items, filters))

This basically checks that every (key, val) of the filter exists in the item

CodePudding user response:

private handleFilters (items, ...props) {
    return items.filter(item => props.every(prop => item[prop] === prop));
}

CodePudding user response:

To use the filter object dynamically, you could look into using something like Object.keys().

Here's your example code changed to use that:

const items = [
    {
        prop1: 'Jotahan',
        prop2: 'London',
        prop3: '1234567'
    },
    {
        prop1: 'Jones',
        prop2: 'Paris',
        prop3: '987'
    }
];

const filters = { prop2: 'Paris', prop3: '987' };

const handleFilters = (items, filters) => {
    return items.filter(item => {
        return Object.keys(filters).some(filterKey => item[filterKey] === filters[filterKey])
    });
}

It loops through the items, the same as you already were doing. However, now it also loops through the keys set in the filters object. The some() function returns a boolean; true if the callback is true and false if the callback is false. Here it checks if the value in item[filterKey] is the same as the value in filters[filterKey].

It's important to note that this returns the item if any of the filter values matches with an item's property. If all values must be in the filtered item, you'll want to use Object.keys(filters).every(filterKey => item[filterKey] === filters[filterKey]). The every function returns true only if all filterKeys have the same value as the keys checked in item.

It is also possible to use Object.entries(), which allows directly using the value, but for this example I chose to use just the keys, for consistency.

CodePudding user response:

You can filter by using Object.key , filter and every method in precise manner.

const items = [
      {
        prop1: 'Jotahan',
        prop2: 'London',
        prop3: '1234567'
      },
      {
        prop1: 'Jones',
        prop2: 'Paris',
        prop3: '987'
      }
    ];
    
    const filters = { prop2: 'Paris', prop3: '987' };
    
    const handleFilter = (items, filters) => {
      return items.filter((item) => Object.keys(filters).every((key) => item[key] === filters[key]));
    }
    
    console.log(handleFilter(items, filters))

CodePudding user response:

You can remove your multiple if blocks by using for..in

const items = [
{
    prop1: 'Jotahan',
    prop2: 'London',
    prop3: '1234567'
},
{
    prop1: 'Jones',
    prop2: 'Paris',
    prop3: '987'
}
];

const filters = { prop2: 'Paris', prop3: '987' };

   const handleFilters = (items, filters) => {
        return items.filter((item) => {
            for(const key in filters) {
                if (filters[key] !== item[key]) {
                    return false;
                }
            }
            return true;
        });
    }
    
console.log(handleFilters(items, filters))

It does the same as your code was doing.

CodePudding user response:

Some Improvement over @David Alvarez approach, here i have used Object.keys instead of Object.entries as Object.key is faster than Object.entries here is the comparison: https://www.measurethat.net/Benchmarks/Show/3685/0/objectentries-vs-objectkeys-vs-objectkeys-with-extra-ar

const items = [
    {
        prop1: 'Jotahan',
        prop2: 'London',
        prop3: '1234567'
    },
    {
        prop1: 'Jones',
        prop2: 'Paris',
        prop3: '987'
    }
];

const filters = {prop2: 'Paris', prop3: '987'};

const handleFilters = (items, filters) => (
    items.filter((item) =>
        Object.keys(filters).every(key => item[key] === filters[key])
    )
);

console.log(handleFilters(items, filters))

  • Related