Home > database >  How to filter through an array of objects and only get the objects whose entries do not equal any en
How to filter through an array of objects and only get the objects whose entries do not equal any en

Time:02-21

Basically I need to do an 'all except' option for when you are filtering table of products. I have two arrays: uniqueProducts and selectedFilters. uniqueProducts contains objects of products that are to be displayed in a table. selectedFilters contains objects of the pre-selected filters.

Here's what an object of the uniqueProducts array looks like:

{category: 'radio', price: '50', manufacturer: 'sony', production_date: '2020/05/30'}

Here's what the objects of the selectedFilters array look like:

{type: 'manufacturer', value: 'sony', status: true}
{type: 'category', value: 'radio', status: true}

Because the price filter has min and max, I will filter through prices once I have the array of objects ready.

Filtering also does not include production_date. So, the selectedFilters objects's type property can be either of the two above.

What I need to do is to filter through the uniqueProducts, and only get the objects that do not match any of the type and value of the objects in selectedFilters.

To put it differently, when you select filters for the products, and click 'all except', you have to see all the products that do not match that filter. Hope you get what I mean to say.

I've tried doing something like this:

uniqueProducts.forEach((product)=>{
                for(let i = 0; i < selectedFilters.length; i  ) {
                    if(product[selectedFilters[i].type] !== selectedFilters[i].value) {
                        allExceptArray.push(product)
                    }
                }
            })

I've tried nesting for..loops too, and various other conditions, but the issue I keep running into is that, every time a particular property of an object DOES NOT match, it gets pushed into the array, even if the other property of that same object DOES match.

Can somebody help me figure out how to do the logic here?

CodePudding user response:

The following approach is based on a single reduce task where the reducer function incorporates a some based filter/comparison task.

The filter/match process is implemented in a way that a match is assumed as soon as the criteria of just one filter-configuration matches a single item property.

function isMatchingItem({ type: key, value }, item) {
  return item[key] === value;
}
function collectNonMatchingItem({ filters = [], result = [] }, item) {
  if (
    !filters.some(filter => isMatchingItem(filter, item))
  ) {
    result.push(item);
  }
  return { filters, result };
}

const uniqueProducts = [
  { category: 'radio', price: '50', manufacturer: 'sony', production_date: '2020/05/30' },
  { category: 'radio', price: '70', manufacturer: 'philips', production_date: '2020/05/30' },
  { category: 'tv', price: '500', manufacturer: 'sony', production_date: '2020/05/30' },
  { category: 'tv', price: '650', manufacturer: 'philips', production_date: '2020/05/30' },
  { category: 'console', price: '900', manufacturer: 'sony', production_date: '2020/05/30' },
  { category: 'console', price: '700', manufacturer: 'nintendo', production_date: '2020/05/30' },
];
const selectedFilters = [
  { type: 'manufacturer', value: 'sony', status: true },
  { type: 'category', value: 'radio', status: true },
];

const result = uniqueProducts.reduce(collectNonMatchingItem, {

  filters: selectedFilters,
  result: [],

}).result;

console.log({ result });
.as-console-wrapper { min-height: 100%!important; top: 0; }

Edit

"... So, I need to now do the case when prices have been selected too and need to be all except filtered. I know I have to filter through the result array, but again, how do I make sure that only the objs that DO NOT match the min/max price are taken? ... My first instinct now is to just reverse the condition, i.e. do if NOT match, but then, what do I put inside? It doesn't feel proper to leave the if block empty. How should I go about doing it?" – user17144382

The final price filtering of cause should be based on filter. Since the OP wants to filter every item where the item's price value is not within a certain min/max price range, one just needs to adapt the price value comparison accordingly. More important, filter like most of the Array methods supports a second argument, the thisArg, which gets applied as context of any filter function which supports this binding.

Thus it's not that difficult to come up with the additional implementation and usage of a properly named filter function ...

function isOutOfBoundPriceRange(item) {
  const { minPrice, maxPrice } = this;
  const price = parseFloat(item.price);
  return (price < parseFloat(minPrice)) || (price > parseFloat(maxPrice));
}

function isMatchingItem({ type: key, value }, item) {
  return item[key] === value;
}
function collectNonMatchingItem({ filters = [], result = [] }, item) {
  if (
    !filters.some(filter => isMatchingItem(filter, item))
  ) {
    result.push(item);
  }
  return { filters, result };
}

const uniqueProducts = [
  { category: 'cellphone', price: '19', manufacturer: 'nokia', production_date: '2020/05/30' },
  { category: 'radio', price: '50', manufacturer: 'sony', production_date: '2020/05/30' },
  { category: 'radio', price: '70', manufacturer: 'philips', production_date: '2020/05/30' },
  { category: 'tv', price: '500', manufacturer: 'sony', production_date: '2020/05/30' },
  { category: 'tv', price: '650', manufacturer: 'philips', production_date: '2020/05/30' },
  { category: 'console', price: '900', manufacturer: 'sony', production_date: '2020/05/30' },
  { category: 'console', price: '700', manufacturer: 'nintendo', production_date: '2020/05/30' },
];
const selectedFilters = [
  { type: 'manufacturer', value: 'sony', status: true },
  { type: 'category', value: 'radio', status: true },
];
const  priceFilter = {
  minPrice: 20,
  maxPrice: 650,
};

const result = uniqueProducts.reduce(collectNonMatchingItem, {

  filters: selectedFilters,
  result: [],

}).result.filter(isOutOfBoundPriceRange, priceFilter);

console.log({ priceFilter, result });
.as-console-wrapper { min-height: 100%!important; top: 0; }

  • Related