JonasLevin
JonasLevin

Reputation: 2089

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,

Upvotes: 2

Views: 9509

Answers (4)

DecPK
DecPK

Reputation: 25398

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

Upvotes: 6

Mechanic
Mechanic

Reputation: 5380

const input = [ { "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 filter = { "level": "2", "wpId": "123" };

const filteredOutput = input.filter( obj => {
  return Object.keys(filter).every( filterKeys => {
    return obj[filterKeys] === filter[filterKeys]
  });
});

console.log(filteredOutput);

Upvotes: 1

Viet
Viet

Reputation: 12779

You can do like this:

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 filterObject = {
  level: "2",
  wpId: "123",
};

const result = data.filter((item) => {
  let flag = true;
  Object.keys(filterObject).forEach((key) => {
    if (item[key] !== filterObject[key]) {
      flag = false;
      return;
    }
  });
  return flag;
});

console.log(result);

Upvotes: 1

Sebastian
Sebastian

Reputation: 1529

This should do the trick!

const exampleData = [
  {
    "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 filterObject1 = {
  "level": "2",
}

const filterObject2 = {
  "level": "2",
  "wpId": "123"
}


function filter(data, filterObject) {
  const filterValues = Object.entries(filterObject)
  
  let filteredData = data
  for(const [filterKey, filterValue] of filterValues) {
    filteredData = filteredData.filter(obj => obj[filterKey] === filterValue)
  }
  return filteredData
}



console.log(filter(exampleData, filterObject1))
console.log(filter(exampleData, filterObject2))

Upvotes: 2

Related Questions