TransWikia.com

How does one create a filter condition based on a configuration object for filtering data-items of an array?

Stack Overflow Asked by frontMaster on January 22, 2021

I have a process which dynamically – depending on checkbox state changes – creates kind of a config object which describes how the condition for filtering an array of data-items should be built.

A filter configuration might look like this one …

const filter = {
  publicationType: ['type-1', 'type-2'],
  termType: ['term-1', 'term-2'],
  reportFormat: ['xml'],
}

A simplified list of data items then looks like that …

const data = [
  { id: 1, reportFormat: 'txt', termType: 'term-1', publicationType: 'type-1' },
  { id: 2, reportFormat: 'xml', termType: 'term-2', publicationType: 'type-2' },
  { id: 3, reportFormat: 'txt', termType: 'term-2', publicationType: 'type-2' },
]

I would like the condition to match every category/type (a configuration’s key), but its value can be either one from a category’s/type’s array.

From the provided example data and specifications the expected filter result would be …

[{ id: 2, reportFormat: 'xml', termType: 'term-2', publicationType: 'type-2' }]

How does an approach look like which builds a correct filter condition based on the provided filter configuration object.

Below is my attempt to make a filter function but filtering works properly only on single Select component, if I try to filter on multiple Select components – data duplicates.

const handleFilter = (val) => {
    const filterKeys = Object.keys(val);
    const filteredStats = [];

    // loop objects in fetched arr
    for (const item of stat) {
      // loop properties by which filtering data

      filterKeys.forEach((keys) => {
        // check if data property match with a filtering property in array

        const isPresent = val[keys].some((key) => {
          const statProperty = item[keys];
          const filterProperty = key;
          return filterProperty === statProperty;
        });
        if (isPresent) {
          filteredStats.push(item);
        }
      });
    }
    setfilteredState(filteredStats);
  };

https://codesandbox.io/s/checkbox-filter-vqex7?file=/src/App.js

One Answer

One of the approache's base techniques is taking advantage of the fact that almost every Array method allows for an additionally passed target object which the solution utilizes with e.g. Array.prototype.filter and Array.prototype.every

The main approach is reading and applying the filter configuration for every iteration of the data array's filter method.

This function needs to iterate every key-value(List) pair of a configuration's entries for checking whether a specific data-item matches a specifically configured condition-entry.

The latter task is done by another function which can access the data-item and does "know" both, this data-item's currently to be checked for property-key and the list of possibly valid property-values, which makes it easy to check for whether some of the final condition(s) do(es) apply.

There is still an edge case, empty value lists, which need to be handled by the latter function. Since the truthiness of its checking relies on some ... (and some's default return value for empty arrays is false ... [].some(x => true) === false) ... one has to handle this scenario separately by explicitly returning true for its fulfilled precondition of valueList.length === 0.

function doesBoundItemMatchConditionEntry([key, valueList]) {
  const item = this;
  const itemValue = item[key];
  return ((
    valueList.length === 0
  ) || (
    item.hasOwnProperty(key) &&
    valueList.some(value => value === itemValue)
  ));
}
function doesItemMatchConditionsOfBoundConfig(item) {
  // const config = this;
  return Object
    .entries(this)
    .every(doesBoundItemMatchConditionEntry, item);
}

const filterConfig = {
  publicationType: ['type-1', 'type-2'],
  termType: ['term-1', 'term-2'],
  reportFormat: ['xml'],
}
const sampleData = [
  { id: 1, reportFormat: 'txt', termType: 'term-1', publicationType: 'type-1' },
  { id: 2, reportFormat: 'xml', termType: 'term-2', publicationType: 'type-2' },
  { id: 3, reportFormat: 'txt', termType: 'term-2', publicationType: 'type-2' },
  { id: 4, reportFormat: 'txt', termType: 'term-2', publicationType: 'type-1' },
  { id: 5, reportFormat: 'xml', termType: 'term-1', publicationType: 'type-2' },
  { id: 6, reportFormat: 'txt', termType: 'term-1', publicationType: 'type-2' },
]

console.log(
  'sampleData.filter(doesItemMatchConditionsOfBoundConfig, filterConfig) ...',
  sampleData.filter(doesItemMatchConditionsOfBoundConfig, filterConfig)
);
console.log(
`sampleData.filter(doesItemMatchConditionsOfBoundConfig, {
    publicationType: ['type-2'],
    termType: ['term-1'],
    reportFormat: [],
}) ...`,
  sampleData.filter(doesItemMatchConditionsOfBoundConfig, {
    publicationType: ['type-2'],
    termType: ['term-1'],
    reportFormat: [],
  })
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Correct answer by Peter Seliger on January 22, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP