首页 > 解决方案 > 如何根据配置对象创建过滤条件以过滤数组的数据项?

问题描述

我有一个动态的过程 - 根据复选框状态的变化 - 创建一种config对象,该对象描述了应该如何构建过滤数据项数组的条件。

过滤器配置可能看起来像这样......

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

一个简化的数据项列表看起来像这样......

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' },
]

我希望条件匹配每个类别/类型(配置的键),但它的值可以是类别/类型数组中的一个。

根据提供的示例数据和规范,预期的过滤结果将是......

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

filter一种基于提供的配置对象构建正确过滤条件的方法看起来如何。

下面是我尝试制作过滤器功能但过滤仅在单个 Select 组件上正常工作,如果我尝试过滤多个 Select 组件 - 数据重复。

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

标签: javascriptarraysdata-structuresdynamicfilter

解决方案


该方法的基本技术之一是利用这样一个事实,即几乎每种Array方法都允许额外传递的target对象,该解决方案与例如Array.prototype.filterArray.prototype.every

主要方法是为数据数组方法的每次迭代读取和应用过滤器配置filter

此函数需要迭代配置的every 键值(列表)对,entries以检查特定数据项是否与特定配置的条件条目匹配。

后一项任务由另一个函数完成,该函数可以访问数据项并且“知道”两者,该数据项当前要检查属性键和可能有效的属性值列表,这使得检查变得容易最终条件是否some适用。

还有一个边缘情况,空值列表,需要由后一个函数处理。由于其检查的真实性依赖于some...(并且some空数组的默认返回值是false... )...必须通过显式返回其满足的前提条件来[].some(x => true) === false单独处理这种情况。truevalueList.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; }


推荐阅读