React - How to compare an filter criteria object with arrays as values to array of objects

Viewed 39

I am trying to filter an array of objects (my product data) with an object that has an array of values (my filter criteria state)

Here is what my filter criteria state data structure looks like:

{
productname: []
designer: []
sizes: []
category: []
}

and this is an example of how my product data object looks like :

[
{
    "id": "10",
    "productName": "M NRG BH Jacket",
    "designer": "NIKE",
    "category": "outerwear",
    "sizes": ["XS", "S", "M", "L", "XL"],
    "color": "limestone",
    "description": "CACT.US CORP...",
    "price": 400,
    "img": [
      "m-nrg-jacket-nike",
      "m-nrg-jacket-nike2",
      "m-nrg-jacket-nike3",
      "m-nrg-jacket-nike4"
    ]
 }
...
]

And this is my current attempt trying to use the reduce function and iterating through each of my filterCriteria's to set state for my displayedProducts component

const [displayedProducts, setDisplayedProducts] = useState(productData);
  const filter = () => {
    let updatedDisplayedProducts = [];
    let count = 0;
    for (const filterKey in filterCriteria) {
      const currFilter = filterCriteria[filterKey];
      productData.reduce((accum, curr) => {
        console.log(currFilter);
        console.log(curr[filterKey]);
        if (!accum) {
          return productData;
        } else if (filterKey === "sizes") {
          if (filterCriteria[filterKey].includes(curr[filterKey])) {
            updatedDisplayedProducts.push(curr);
          }
        } else if (currFilter.includes(curr[filterKey].toUpperCase())) {
          updatedDisplayedProducts.push(curr);
        }
        console.log(updatedDisplayedProducts);
        setDisplayedProducts(updatedDisplayedProducts);
      }, []);
    }
  };

I've tried some other solutions but I always came across an issue where I was essentially nesting too many for loops and it was mutating my state somehow. Any help is greatly appreciated!

1 Answers

It's difficult to understand what you want to do exactly with ur code.

in my code, I supposed that

  • a product will be displayed only if it satisfies all filterCriteria
  • if any size (e.g. 'XS') in filterCriteria is included in actual product's size, that product will be displayed

explanation

I made new array (which is copy of state but new reference), then removed any product which does not satisfy filter

import React, { useState } from 'react';

type Product = {
  id: string
  productName: string
  designer: string
  category: string
  sizes: string[]
  color: string
  description: string
  price: number
  img: string[]
}

const products: Product[] = [
  {
    'id': '1',
    'productName': 'a',
    'designer': 'NIKE',
    'category': 'outerwear',
    'sizes': ['S', 'M', 'L', 'XL'],
    'color': 'limestone',
    'description': 'CACT.US CORP...',
    'price': 400,
    'img': [
      'm-nrg-jacket-nike',
      'm-nrg-jacket-nike2',
      'm-nrg-jacket-nike3',
      'm-nrg-jacket-nike4'
    ]
  },
  {
    'id': '2',
    'productName': 'b',
    'designer': 'NIKE',
    'category': 'outerwear',
    'sizes': ['XS', 'S', 'M', 'L'],
    'color': 'red',
    'description': 'CACT.US CORP...',
    'price': 400,
    'img': [
      'm-nrg-jacket-nike',
      'm-nrg-jacket-nike2',
      'm-nrg-jacket-nike3',
      'm-nrg-jacket-nike4'
    ]
  },
  {
    'id': '3',
    'productName': 'abc',
    'designer': 'APPLE',
    'category': 'outerwear',
    'sizes': ['XS', 'S', 'M', 'L', 'XL'],
    'color': 'blue',
    'description': 'CACT.US CORP...',
    'price': 400,
    'img': [
      'm-nrg-jacket-nike',
      'm-nrg-jacket-nike2',
      'm-nrg-jacket-nike3',
      'm-nrg-jacket-nike4'
    ]
  }
]

const filterCriteria = {
  productName: [] as string[],
  designer: [] as string[],
  sizes: ['XS'] as string[],
  category: [] as string[],
}

function App() {
  const [displayedProducts, setDisplayedProducts] = useState(products);

  const filter = () => {
    const updatedDisplayedProducts = displayedProducts.filter(currentProduct=>{
      
      const shouldDisplayed = (Object.keys(filterCriteria) as (keyof typeof filterCriteria)[]).every((filterKey)=>{
        const currFilterValues = filterCriteria[filterKey];
        console.log(`filter: ${filterKey}: currFilterValues`)

        if (currFilterValues.length === 0) return true

        if (filterKey === 'sizes') {
          return currFilterValues.some(item => currentProduct[filterKey].includes(item))
        } else {
          return currFilterValues.includes(currentProduct[filterKey].toUpperCase())
        }
      })
      console.log(`shouldDisplayed: ${shouldDisplayed}`)

      return shouldDisplayed
   })

    setDisplayedProducts(updatedDisplayedProducts)
  };
  
  return (
    <div className="App">
      <button onClick={()=>filter()}>filter!</button>
      {displayedProducts.map(item => <div key={item.id}>{item.productName}</div>)}
    </div>
  );
}

export default App;

Related