首页 > 解决方案 > filter an array of objects based on an object with an array as it's property value in react

问题描述

I'm trying to create a filter function that receives two arguments: an array (which is going to be filtered) and an object which is the filter criteria.

Consider the array structure as:

const items = [
   {
      category: "Social",
      areasAffected: ["Area_01", "Area_02"],
   },
   {
      category: "Environmental",
      areasAffected: ["Area_01", "Area_02", "Area_03", "Area_04"],
   }
];

and, consider the object as below:

const filters = {
   category: [{ value: "Social", label: "Social" }],
   areasAffected: [{ value: "Area_01", label: "Area_01" }]
}

and I have defined the filter function as:

const filterChallenges = (items, filters) => {
const filterKeys = Object.keys(filters);
 
return items.filter((item) => {
  filterKeys.forEach((key) => {
    if (!Array.isArray(item[key])) {
      return item[key]
        .toString()
        .toLowerCase()
        .includes(
          filters[key].map((filterEle) =>
            filterEle.value.toString().toLowerCase()
          )
        );
    } else {
      return item[key].map((arrEle) => {
        arrEle
          .toString()
          .toLowerCase()
          .includes(
            filters[key].map((filterEle) =>
              filterEle.value.toString().toLowerCase()
            )
          );
      });
    }
  });
});
};

when I run the function, it returns an empty array. Does anybody have any suggestion?

标签: javascriptfilter

解决方案


代码本身有两个问题。首先,forEach不返回值。Array.every()如果您想要求所有过滤器都匹配,您将需要在此处使用。否则你会想要使用Array.some().

其次,String.includes()不采用数组,而是采用字符串。在这里,Array.every()如果您希望过滤器数组的所有项目都匹配该值,则需要再次使用,否则使用Array.some(). 至于else处理作为数组的项值的分支,Array.some()如果您只希望其中一个数组值匹配,则需要使用。

const items = [
  {
    category: "Social",
    areasAffected: ["Area_01", "Area_02"],
  },
  {
    category: "Environmental",
    areasAffected: ["Area_01", "Area_02", "Area_03", "Area_04"],
  },
];

const filterChallenges = (items, filters) => {
  const filterKeys = Object.keys(filters);

  return items.filter((item) => {
    // ALL of the filters must be matched (.every())
    return filterKeys.every((filterKey) => {
      if (!Array.isArray(item[filterKey])) {
        const candidateValue = item[filterKey].toString().toLowerCase();
        // ALL of the filter values must be included in the item value
        // (.every())
        return filters[filterKey].every((filterEle) =>
          candidateValue.includes(filterEle.value.toString().toLowerCase())
        );
      }

      // Value is an array, ONE element (.some()) must match ALL of the
      // filter values (.every())
      return item[filterKey].some((candidate) => {
        const candidateValue = candidate.toString().toLowerCase();
        return filters[filterKey].every((filterEle) =>
          candidateValue.includes(filterEle.value.toString().toLowerCase())
        );
      });
    });
  });
};

const ret = filterChallenges(items, {
  category: [{ value: "Social", label: "Social" }],
  areasAffected: [{ value: "Area_01", label: "Area_01" }],
});

console.log(ret);

此外,如果您的所有值都是字符串,那么.toString()是多余的。


推荐阅读