-2

I have 2 arrays structured like this :

const products = [
  {
    id: 1,
    name: 'Table',
    attributes: [
      { id: 13, prodId: 1, attribute_id: 1, attribute_value_id: 8 },
      { id: 14, prodId: 1, attribute_id: 2, attribute_value_id: 19 },
      { id: 15, prodId: 1, attribute_id: 3, attribute_value_id: 88 },
      { id: 16, prodId: 1, attribute_id: 4, attribute_value_id: 237 },
    ],
  },
  {
    id: 2,
    name: 'Chair',
    attributes: [
      { id: 25, prodId: 2, attribute_id: 1, attribute_value_id: 2 },
      { id: 26, prodId: 2, attribute_id: 2, attribute_value_id: 21 },
      { id: 27, prodId: 2, attribute_id: 3, attribute_value_id: 127 },
      { id: 28, prodId: 2, attribute_id: 4, attribute_value_id: 240 },
    ],
  },
  {
    id: 3,
    name: 'Couch',
    attributes: [
      { id: 41, prodId: 3, attribute_id: 1, attribute_value_id: 8 },
      { id: 42, prodId: 3, attribute_id: 2, attribute_value_id: 18 },
      { id: 43, prodId: 3, attribute_id: 3, attribute_value_id: 88 },
      { id: 44, prodId: 3, attribute_id: 5, attribute_value_id: 271 },
    ],
  },
  {
    id: 4,
    name: 'Rug',
    attributes: [
      { id: 75, prodId: 4, attribute_id: 1, attribute_value_id: 2 },
      { id: 76, prodId: 4, attribute_id: 2, attribute_value_id: 19 },
      { id: 77, prodId: 4, attribute_id: 3, attribute_value_id: 89 },
      { id: 78, prodId: 4, attribute_id: 4, attribute_value_id: 256 },
    ],
  },
]

const filters = [
  { attribute_id: 1, attribute_value_id: '8' },
  { attribute_id: 3, attribute_value_id: '88' },
]

How can I filter objects from the "products" array that matches at least all criteria from the "filters" array, no matter how many products are there, nor how many attributes each product has ?

In the above case, I would like to get the result :

[{
  id: 1,
  name: 'Table',
  attributes: [
    { id: 13, prodId: 1, attribute_id: 1, attribute_value_id: 8 },
    { id: 14, prodId: 1, attribute_id: 2, attribute_value_id: 19 },
    { id: 15, prodId: 1, attribute_id: 3, attribute_value_id: 88 },
    { id: 16, prodId: 1, attribute_id: 4, attribute_value_id: 237 },
  ],
},
{
  id: 3,
  name: 'Couch',
  attributes: [
    { id: 41, prodId: 3, attribute_id: 1, attribute_value_id: 8 },
    { id: 42, prodId: 3, attribute_id: 2, attribute_value_id: 18 },
    { id: 43, prodId: 3, attribute_id: 3, attribute_value_id: 88 },
    { id: 44, prodId: 3, attribute_id: 5, attribute_value_id: 271 },
  ],
}]

Edit : I've tried to start from something like this :

const filteredProducts = products.filter((p)=>{
    return filters.some((f)=>{
        // another loop ?
    });
});
console.log(filteredProducts);

but couldn't manage to loop through each product's nested attributes and through filters when there's many of them

NB : product "couch" having an attribute with "attribute_id" of 5 instead of 4 is not a typo

Hoping I'm clear enough, thanks in advance !

Milk
  • 23
  • 4
  • 1
    What have you tried, and what exactly is the problem with it? – jonrsharpe Feb 20 '22 at 13:44
  • 1
    You should give examples that show the behaviour when one property or one record matches,... Now you essentially only show data that has all or nothing, so it doesn't really demonstrate the nuance of AND/OR behaviour. – trincot Feb 20 '22 at 13:52
  • Does this answer your question? [Filtering array of objects with arrays based on nested value](https://stackoverflow.com/questions/38375646/filtering-array-of-objects-with-arrays-based-on-nested-value) – pilchard Feb 20 '22 at 13:59
  • @pilchard thanks for the link, which is the kind of starter i used, but as trincot explained i needed a every() loop instead of a some() one – Milk Feb 20 '22 at 14:40

1 Answers1

-1

Yes, you need another loop.

From what you've said ... at least all criteria from the "filters", you'd have to use filters.every and not filters.some. And then for each you'll have to iterate over the key value pairs (Object.entries) and see if any of the objects in the attributes array matches (use some).

So use combinations of every and some`, like so:

function filterByAttributes(products, filters) {
    return products.filter(({attributes}) => 
        filters.every(filter => 
            Object.entries(filter).every(([key, value]) =>
                attributes.some(obj => obj[key] == value)
            )
        )
    );
}

const products = [{id: 1,name: 'Table',attributes: [{ id: 13, prodId: 1, attribute_id: 1, attribute_value_id: 8 },{ id: 14, prodId: 1, attribute_id: 2, attribute_value_id: 19 },{ id: 15, prodId: 1, attribute_id: 3, attribute_value_id: 88 },{ id: 16, prodId: 1, attribute_id: 4, attribute_value_id: 237 },],},{id: 2,name: 'Chair',attributes: [{ id: 25, prodId: 2, attribute_id: 1, attribute_value_id: 2 },{ id: 26, prodId: 2, attribute_id: 2, attribute_value_id: 21 },{ id: 27, prodId: 2, attribute_id: 3, attribute_value_id: 127 },{ id: 28, prodId: 2, attribute_id: 4, attribute_value_id: 240 },],},{id: 3,name: 'Couch',attributes: [{ id: 41, prodId: 3, attribute_id: 1, attribute_value_id: 8 },{ id: 42, prodId: 3, attribute_id: 2, attribute_value_id: 18 },{ id: 43, prodId: 3, attribute_id: 3, attribute_value_id: 88 },{ id: 44, prodId: 3, attribute_id: 5, attribute_value_id: 271 },],},{id: 4,name: 'Rug',attributes: [{ id: 75, prodId: 4, attribute_id: 1, attribute_value_id: 2 },{ id: 76, prodId: 4, attribute_id: 2, attribute_value_id: 19 },{ id: 77, prodId: 4, attribute_id: 3, attribute_value_id: 89 },{ id: 78, prodId: 4, attribute_id: 4, attribute_value_id: 256 },],},]

const filters = [{ attribute_id: 1, attribute_value_id: '8' },{ attribute_id: 3, attribute_value_id: '88' },];

console.log(filterByAttributes(products, filters));
trincot
  • 317,000
  • 35
  • 244
  • 286