Home > Blockchain >  Filter the filtered array and render
Filter the filtered array and render

Time:04-10

How can I filter the filtered array? So let's say that I filter the products by price. Assume the value of the input range slider (price) is less than a thousand dollars then it will return an array that is less than the said value which is a thousand dollars. Now, if I want to filter again using the brand or category filter buttons, so for example I chose the brand 'ikea' from the brand filters. How can I filter the returned array from the filtered price? Right now, when I filter the products by the price it can return an array but when I filter again by category/brand then it will return an array that is not coming from the filtered price array.

Here are my codes:

export const state = {
  products: [], //this is where I stored all the products coming from the API
  filters: {
    categories: [], //this is where I stored all the categories
    brands: [], //this is where I stored all the brand
    price: 0, //this is where I stored the max price from the array
  },
};

@param value - an integer value that comes from the input range slider

export const loadFilterPrice = function (value) {
  return state.products.filter(product => product.price <= value);
};

@param type - a string value 'brand' or 'category' @param value - a string value of brands/categories,

export const loadFilterProductByType = function (type, value) {
  if (value === 'all') return state.products;

  return state.products.filter(product => product[type] === value);
};

CodePudding user response:

You filtered products but you haven't assigned a new variable/state for filtered products

You can add a filteredProducts variable here

export const state = {
  products: [], //this is where I stored all the products coming from the API
  filteredProducts: [] //this should be a clone of `products`
  filters: {
    categories: [], //this is where I stored all the categories
    brands: [], //this is where I stored all the brand
    price: 0, //this is where I stored the max price from the array
  },
};

Whenever you assign a value for products from an API call initially, you can assign filteredProducts value too

//`respondedProducts` is from API calls
state.products = [...respondedProducts]
state.filteredProducts = [...respondedProducts]

Here is how we use it

export const loadFilterPrice = function (value) {
  if(!value) return state.products; //return the original product list

  return state.filteredProducts.filter(product => product.price <= value);
};
state.filteredProducts = loadFilterPrice(1000) //need to update filtered products variable

Similarly, we can apply it to loadFilterProductByType

export const loadFilterProductByType = function (type, value) {
  if (value === 'all') return state.products; //return the original product list

  return state.filteredProducts.filter(product => product[type] === value);
};
state.filteredProducts = loadFilterProductByType('brand', 'ikea') //need to update filtered products variable

CodePudding user response:

This solution specifically attempts to answer below point:

What should I do?

There are several ways to go about this. The optimal ways may be to use useMemo, useCallback, etc.

The below is just one way that it may be done, which relies only on useState and useEffect.

const {useState, useEffect} = React;

const Thingy = ({initState, ...props}) => {
  const [products, setProducts] = useState(
    [
      ...initState.products.map(
        x => ({...x, filtered: false})
      )
    ]
  );
  const [filters, setFilters] = useState({
    categories: "", brands: "", price: 0 // Number.MAX_VALUE
  });
  const [showFilters, setShowFilters] = useState(false);
  const [filterPrice, setFilterPrice] = useState(0);
  const renderFilterFor = col => (
    <div>
      <label> {col} </label>
      <select
        name={col}
        onChange={e => {
          const v = e.target.value;
          setFilters(prev => ({
            ...prev,
            [col]: v !== 'None' ? v : ""
          }));
        }}
      >
        {
          [
            'None', ...new Set(products.map(ob => ob[col]))
          ].map(cat => (
            <option value={cat}>{cat}</option>
          ))
        }
      </select>
    </div>
  );
  const applyFilter = pr => (
    Object.entries(filters)
    .filter(([k, v]) => ((k === 'price' && v > 0) || v.length > 0))
    .every(([k, v]) => ((k === 'price' && pr[k] <= v) || pr[k] === v))
  );
  useEffect(
    () => setProducts(
      prev => prev.map(
        p => ({
          ...p,
          filtered: applyFilter(p)
        })
      )
    ),
    [filters]
  );
  return (
    <div>
      <button
        onClick={() => setShowFilters(prev => !prev)}
      >
        {showFilters ? 'Hide' : 'Show'} Filters
      </button>
      {
        showFilters && (
          <div >
            <div>{renderFilterFor("categories")}</div>
            <div>{renderFilterFor("brands")}</div>
            <label for="priceInp">Enter price: </label>
            <input
              type="number" value={filterPrice} min={0}
              onChange={e => {
                const v = e.target.value;
                setFilterPrice(v);
                setFilters(prev => ({
                  ...prev,
                  price:  v
                }));
              }}
            />
          </div>
        )
      }
      {
        products.filter(({filtered}) => filtered).map(
          ({productId, productName, categories, brands, price}) => (
            <div  key={productId}>
              {productName}
              <b>Category: </b>{categories}
              <b>Brand: </b>{brands}
              <b>Price: </b>{price}
            </div>
          )
        )
      }
    </div>
  );
};

const initState = {
  products: [...Array(15).keys()].map(i => ({
    productId: i 1,
    productName: `Product - ${i 1}`,
    categories: `Category - ${i % 3  1}`,
    brands: `Brand - ${i % 4  1}`,
    price: Math.floor(Math.random() * ((i 1) * 5))
  }))
};

ReactDOM.render(
  <div>
    DEMO
    <Thingy initState={initState}/>
  </div>,
  document.getElementById("rd")
);
.singleProduct {
  border: 2px solid grey;
  margin: 5px;
  width: 85%;
}
.singleProduct > *{
  margin: 15px;
  padding: 5px;
}

.filtersLine {
  display: flex;
  margin: 15px;
  align-items: center;
  justify-content: space-between;
  width: 85%;
}
.filtersLine > label { margin: 10px; }
<div id="rd" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>

  • Related