Filter javascript objects array based on a multivalue object

Viewed 54

Need to filter array based on a filter object having multiple values consisting arrays

    var vendors = [
      {
        vendor_name: "John",
        region: ["APAC", "UKDE"],
        scanned_status: "Yes",
        channel: ["FILE", "API"],
        pii_attributes: ["A", "B"]
      },
      {
        vendor_name: "Onir",
        region: ["APAC", "LATAM"],
        scanned_status: "No",
        channel: ["FILE"],
        pii_attributes: ["A", "C"]
      },
      {
        vendor_name: "Suresh",
        region: ["UKDE", "NA"],
        scanned_status: "Yes",
        channel: ["API"],
        pii_attributes: ["C", "B"]
      }
    ];

    var filterCriteria = {
      region: ["APAC", "LATAM"], // 'APAC', 'LATAM', 'NA', 'UKDE'
      channel: ["API"], // 'API', 'FILE'
      attributes: ["A", "B"],
      scan_status: "Yes" // 'Yes', 'No', 'All'
    };
    let filtered_vendors = [];
    var result = vendors.filter((el, index, arr) => {
      if (
        filterCriteria["region"].includes(el["region"]) &&
        filterCriteria["channel"].includes(el["channel"]) &&
        filterCriteria["attributes"].includes(el["pii_attributes"])
      ) {
        filtered_vendors.push(el);
        return true;
      }
      return false;
    });

    console.log(result);
    console.log(filtered_vendors);

But I am not able to apply multiple iteration at the filter level for including the elements. WOuld like to know efficient way to filter the array.

3 Answers

You could take a dynamic approach by using same keys for filterCriteria as well.

This approach checks if the filter value is an array and iterates the values and checks with includes.

If no array then it compares the value directly.

var vendors = [{ vendor_name: "John", region: ["APAC", "UKDE"], scanned_status: "Yes", channel: ["FILE", "API"], pii_attributes: ["A", "B"] }, { vendor_name: "Onir", region: ["APAC", "LATAM"], scanned_status: "No", channel: ["FILE"], pii_attributes: ["A", "C"] }, { vendor_name: "Suresh", region: ["UKDE", "NA"],
scanned_status: "Yes", channel: ["API"], pii_attributes: ["C", "B"] }],
    filterCriteria = { region: ["APAC", "LATAM"], channel: ["API"], pii_attributes: ["A", "B"], scanned_status: "Yes" },
    filters = Object.entries(filterCriteria),
    result = vendors.filter(o => filters.every(([k, value]) => Array.isArray(value)
        ? value.some(v => o[k].includes(v)) // change to every if all have to match
        : o[k] === value
    ));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

var data = [{
    "id": 1,
    "term_id": 5,
    "type": "car"
  },
  {
    "id": 2,
    "term_id": 3,
    "type": "bike"
  },
  {
    "id": 3,
    "term_id": 6,
    "type": "car"
  }
];

var result = data.filter(function(v, i) {
  return ((v["term_id"] == 5 || v["term_id"] == 6) && v.type == "car");
})

console.log(result)

I had to interpret your question a little. So, in the following code, we check if for every filter if all elements of the filter property value are also in the given vendor object property (basically a subset operation). In case you meant any element for every filter you need to use an intersection operation.

To implement this, we can use reduce for the iteration and sum up individual checks with && (function isSupersetOf). To apply this for every filter property, we can apply the same technique on the entries set (function isMatch).

Note that I also had to adjust the property names for this to work.

Code:

var vendors = [
      {
        vendor_name: "John",
        region: ["APAC", "UKDE"],
        scanned_status: "Yes",
        channel: ["FILE", "API"],
        pii_attributes: ["A", "B"]
      },
      {
        vendor_name: "Onir",
        region: ["APAC", "LATAM"],
        scanned_status: "No",
        channel: ["FILE"],
        pii_attributes: ["A", "C"]
      },
      {
        vendor_name: "Suresh",
        region: ["UKDE", "NA"],
        scanned_status: "Yes",
        channel: ["API"],
        pii_attributes: ["C", "B"]
      },
      // Add a match for illustration
      {
        vendor_name: "Denep",
        region: ["APAC", "LATAM", "NA"],
        scanned_status: ["Yes"],
        channel: ["API"],
        pii_attributes: ["A", "B", "C"]
      }
    ];

    const filterCriteria = {
      region: ["APAC", "LATAM"], // 'APAC', 'LATAM', 'NA', 'UKDE'
      channel: ["API"], // 'API', 'FILE'
      pii_attributes: ["A", "B"],
      scanned_status: ["Yes"] // 'Yes', 'No', 'All'
    };
    const isSupersetOf = (subset, superset) => 
      subset.reduce((result, next) => result && superset.includes(next), true);
    const isMatch = (filter, element) => 
      Object.entries(filter)
        .reduce((result, [property, values]) => result && isSupersetOf(values, element[property] || []), true)
    
    const filteredVendors = vendors.filter((el, index, arr) => 
      isMatch(filterCriteria, el))

    console.log(filteredVendors);

Related