emptyobjects3
emptyobjects3

Reputation: 1

Filter array by object property values

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.

Upvotes: 0

Views: 116

Answers (6)

Prashant Shah
Prashant Shah

Reputation: 246

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

Upvotes: 0

ClassHacker
ClassHacker

Reputation: 394

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.

Upvotes: 0

Talha Ahmad
Talha Ahmad

Reputation: 11

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

Upvotes: 0

DubiousMaster
DubiousMaster

Reputation: 85

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.

Upvotes: 0

David Alvarez
David Alvarez

Reputation: 1286

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

Upvotes: 2

ysurilov
ysurilov

Reputation: 300

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

Upvotes: 0

Related Questions