Home > front end >  Filtering array objects with nested properties from another array objects properties in Javascript
Filtering array objects with nested properties from another array objects properties in Javascript

Time:02-20

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 !

CodePudding user response:

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));

  • Related