How do I filter data in different categories [SOLVED]

Hi all,

I have a challenge to solve where I am building a filter component that can filter data across multiple categories and return unique results. Functionality wise, it’s pretty much like what you see on eCommerce websites.

Currently, I have gotten to the point where I can filter items across categories using an OR condition but I need to build an AND condition across categories.

So, the data I am working with are called “services”. Here is how I have organised the data (each time a filter item is checked, it gets pushed into this object

So, now I’m wondering how I can find an intersection / union (I’m not sure what algorithm I need) across all services in order to find service/s that match ALL categories.

For example, given the screenshot above, a service item would need:

  • Type === Licence
  • Topic === 'Supplying Government' || 'Privacy'
  • Level === 'Local'
  • Agency === 'Australian Taxation Office' || 'Chief Health Office Branch'

This is a typical data object that I need to work with(called a service).

{
  "__typename": "Service",
  "Id": 26,
  "Jurisdiction": "AG",
  "ServiceType": "Licence",
  "TierOfGovernment": "Commonwealth",
  "Name": "Australian Business Number Registration",
  "DescriptiveName": "Australian Business Number (ABN) Registration",
  "ServiceTopics": [
    {
      "__typename": "ServiceTopic",
      "TopicId": 52,
      "TopicName": "General Taxation Requirements and Registrations"
    }
  ],
  "ServiceOrganisations": [
    {
      "__typename": "ServiceOrganisation",
      "Id": 387618,
      "ServiceId": 26,
      "OrganisationName": "Australian Taxation Office",
      "Sequence": 10,
      "Group": "1"
    },
    {
      "__typename": "ServiceOrganisation",
      "Id": 387619,
      "ServiceId": 26,
      "OrganisationName": "Australian Business Register",
      "Sequence": 20,
      "Group": "1"
    }
  ]
}

Here is the component UI (just for clarity):

Thank you for any help or direction in advance! Would an intersection / union algorithm be appropriate? And how can I build a dynamic filter function across multiple data points?

I tried looking into JSON Path (https://github.com/JSONPath-Plus/JSONPath) but I’m not sure exactly how to best use it. Here is how I do the lazy filter function:

const lazyFilterMatch = services.filter((service: Service) => {
    return filters.some((filter: ServiceFilterItem) => {
      // search deep in an object to get it's value
      const values = JSONPath({ path: filter.serviceSearcher, json: service });
      const valueMatch = values.includes(filter.searchTerm);

      // check the filters against the config to get the correct serviceSearcher path
      const filterLabelMatch = resultFilterConfig.find(
        (item) => item.serviceSearcher === filter.serviceSearcher,
      );

      // build an object to organise data & intersect / union results later
      if (!categories[filterLabelMatch.filterLabel][filter.searchTerm]) {
        categories[filterLabelMatch.filterLabel][filter.searchTerm] = [];
      } else if (valueMatch) {
        categories[filterLabelMatch.filterLabel].searchPath = filter.serviceSearcher;
        categories[filterLabelMatch.filterLabel][filter.searchTerm].push(service);
        return valueMatch;
      }

      return false;
    });
  });

Any help or direction would be GREATLY appreciated! Thank you!

Ok, I think I got it, but need to do some thorough tests :slight_smile: Here is what I came up with:

 /**
 * Filter list of ABLIS services with a given list of filters.
 *
 * Notes:
 * - A serviceSearcher is the "category" to filter by (ie) Topic, Level, Type, Agency
 * - When selecting multiple filters across "cateogies", the filter uses an AND condition.
 * - When selecting multiple filters inside a serviceSearcher, the filter uses an OR condition.
 *
 * @param services List of ABLIS Service records
 * @param filters List of Filter definition
 */
export function filterServices(services: Service[], filters: ServiceFilterItem[]): Service[] {
  // 1. Strict filter match across multiple cateogory selections
  const strictMatch = services.filter((service: Service) => {
    const flattenedFilters = new Set(filters.map((filter) => filter.searchTerm));
    // loop through category items to create an AND condition (intersection using the searchPath)
    const match = filters.every((path) => {
      // for every search path check all services from lazy filter search
      const values: string[] = JSONPath({
        path: path.serviceSearcher,
        json: service,
      });
      // check if any of the checked filter values for each category are shared
      return flattenedFilters.has(values[0]);
    });

    return match;
  });

  return strictMatch;
}
1 Like