Filter an array of objects by another object of filters

I have an array of objects who follow this structure below:

{
  "level": 1
  "objectId": "3756"
  "objectIdNo": 35636
  "wpId": "3635473"
}

I now want to filter an array of these objects by another object. This filterObject would have the structure below:

// filterObject
{
  level: "2"
  objectId: "10"
  wpId: "1"
}

But this filterObject doesn’t always have all the key-value pairs because they get set manually in the UI. As a result the filterObject can also look like this:

{
  level: "2"
}

My goal is to return a new array of filteredObjects who match this filterObject. When only one filter exists on the filterObject I want to return all objects that match this one key-value pair. But if more filters exist on the filterObject I want to return all objects that match both key-value pairs (not only one).

Example:

This is the data I want to filter:

[
  {
    "level": "1"
    "objectId": "11"
    "objectIdNo": "320"
    "wpId": "123"
  },
  {
    "level": "2"
    "objectId": "12"
    "objectIdNo": "321"
    "wpId": "123"
  },
  {
    "level": "2"
    "objectId": "13"
    "objectIdNo": "322"
    "wpId": "120"
  },
]

1.

If this is my filterObject:

{
  "level": "2"
}

Return:

[
  {
    "level": "2"
    "objectId": "12"
    "objectIdNo": "321"
    "wpId": "123"
  },
  {
    "level": "2"
    "objectId": "13"
    "objectIdNo": "322"
    "wpId": "120"
  },
]

2.

If this is my filterObject:

{
  "level": "2",
  "wpId": "123"
}

Return:

[
  {
    "level": "2"
    "objectId": "12"
    "objectIdNo": "321"
    "wpId": "123"
  },
]

I hope that explains the logic I want to achieve which I couldn’t implement myself. I would appreciate some ideas or applicable functions.

This is what I already tried in React. The data variable holds the array of objects and the filter variable hold the filterObjects.

useEffect(() => {
    if (data) {
      const filtered = data.filter((task) => {
        if (!filter) {
          return true;
        }
        return (
          task.level === filter.level ||
          task.objectId === filter.objectId ||
          task.wpId === filter.wpId
        );
      });
      setFilteredTasks(filtered);
    }
  }, [filter]);

With my attempt, if I just set the one filter key-value pair I get an empty array,

Answer

You can achieve this result using filter, Object.keys, and every.

You have to use filter and pass predicate that tell whether it is included in the final result.

In predicate, loop over all properties on the filters object and match if it is present in data or not. Simple

data.filter((o) =>Object.keys(filters).every((k) => filters[k] === o[k]));

const data = [{
    level: "1",
    objectId: "11",
    objectIdNo: "320",
    wpId: "123",
  },
  {
    level: "2",
    objectId: "12",
    objectIdNo: "321",
    wpId: "123",
  },
  {
    level: "2",
    objectId: "13",
    objectIdNo: "322",
    wpId: "120",
  },
];

const filters = {
  level: "2",
  wpId: "123",
};

const result = data.filter((o) =>
  Object.keys(filters).every((k) => filters[k] === o[k])
);
console.log(result);