user16107699
user16107699

Reputation:

Filter an array of objects with a nested array

I am trying to make a filter for this information, this is an array of objects from a API with the information of all the products in the database, in this example I only show one product, but the API request all products.

[
        {
            "Id": 11,
            "Name": "Papas fritas",
            "Description": "Papas",
            "Availability": 1,
            "Price": 1,
            "Stock": 10,
            "Promotion": 0,
            "Discount": 0,
            "Visible": 1,
            "CreationDate": "2021-04-22T18:51:51.000Z",
            "UpdateAt": null,
            "Photo": [
                {
                    "Id": 21,
                    "Photo": "photo.png"
                }
            ],
            "Rating": 4.5,
            "Category": [
                {
                    "Id": 2,
                    "Category": "Moda",
                    "Icon": "Fashion"
                },
                {
                    "Id": 3,
                    "Category": "Fitness",
                    "Icon": "Fitness"
                }
            ],
            "Subcategory": [],
            "Characteristics": [],
  
}

Now I make a class that have the methods for short and filter this array, but I have a problem filtering the information, I don't know how to filter the information by category using something like this:

[{Category: 'Art,Fitnness'}]

It's also is possible that the Promotion property will be formatted this way:

[{Promotion: 'true'}]

As you can see this array with the filters can have more than one property and can be a different property than category i need to validate the values because all values in the array are strings and sometimes Array.filter doesn't work comparing a string "true" === 1

What I implement for this is this function, query is the array of the products and queryvalues is the filter array

FilterFuntion(Query, QueryValues)
{
    QueryValues = Object.entries(QueryObj).map((e) => ( { [e[0]]: e[1] } ));
    
    let FilteredArray = Query;

    QueryValues.map((Filter) =>
    {
        var key = Object.keys(Filter)[0];

        FilteredArray = ValidateFilter(key, Filter[key], FilteredArray);
    });

    return FilteredArray;
}

ValidateFilter(Key, Value, array)
{
        return array.filter((El) => 
        {
            if (Value === "true" || Value === "false")
            {
                return Boolean(El[Key]) === Boolean(Value);
            }

            if(Array.isArray((El[Key]))
            {
                 (El[Key]).filter((sub) => 
                 {
                     return String(sub[Key]) === String(Value);
                 });           
            }

            return String(El[Key]) === String(Value);
        });
}

So I validate if the value of the filter is a bool string or int first and next I return the value that match, that work correct whit all the normal values but when the value nested in another array doesn't work, it returns a filter array of category instead of return the entire product, I don't know how to make this work when there are nested arrays, and when I would want to filter on multiple categories, how can I do it ?.

[{Category: 'Art,Fitnness'}]

Another thing is for example when the filter has a value between two numbers, for example look Rating property, I need to filter and return only the products that have a rating between 5 and 4

[{Promotion: 'true'}, {Category: 'Fitness'}, {Rating: '5,4'}]

Upvotes: 1

Views: 496

Answers (1)

Kinglish
Kinglish

Reputation: 23664

Using filter, we can take each object from the array and test whether or not it meets the criteria.

Here is the anatomy of the filter object:

filter = {
      Rating: [3, 5],
      Category: [
        ['Fitness','Moda'],
        ['OtherCategory']
      ],
      Promotion: 'false'
    }

Rating is an array for 'between' - if you're looking for an exact number, just put it in twice ([4.5,4.5]). Category is an array of arrays and takes care of or/and like this:

  [
    ['Fitness','Moda'], // product has both categories 'Fitenss' and 'Moda'
    ['OtherCategory'] // OR product has 'OtherCategory'
  ]

const data = [{
    "Id": 11,
    "Name": "Papas fritas",
    "Description": "Papas",
    "Availability": 1,
    "Price": 1,
    "Stock": 10,
    "Promotion": 0,
    "Discount": 0,
    "Visible": 1,
    "CreationDate": "2021-04-22T18:51:51.000Z",
    "UpdateAt": null,
    "Photo": [{
      "Id": 21,
      "Photo": "photo.png"
    }],
    "Rating": 4.5,
    "Category": [{
        "Id": 2,
        "Category": "Moda",
        "Icon": "Fashion"
      },
      {
        "Id": 3,
        "Category": "Fitness",
        "Icon": "Fitness"
      }
    ],
    "Subcategory": [],
    "Characteristics": []

  },
  {
    "Id": 13,
    "Name": "Papas fritas2",
    "Description": "Papas2",
    "Availability": 1,
    "Price": 1,
    "Stock": 10,
    "Promotion": 0,
    "Discount": 0,
    "Visible": 1,
    "CreationDate": "2021-04-22T18:51:51.000Z",
    "UpdateAt": null,
    "Photo": [{
      "Id": 21,
      "Photo": "photo.png"
    }],
    "Rating": 3.5,
    "Category": [{
      "Id": 2,
      "Category": "OtherCategory",
      "Icon": "Fashion"
    }],
    "Subcategory": [],
    "Characteristics": []

  }
]

//[{Promotion: 'true'}, {Category: 'Fitness'}, {Rating: '5,4'}]


const filterProductsBy = (criteria, data) => {
  let p = data.filter(obj => {
    let isMatch = true;
    if (criteria.Rating && (obj.Rating < criteria.Rating[0] || obj.Rating > criteria.Rating[1])) isMatch = false;
    if (criteria.Category) {
      let catMatch = false
      // prepare the cat array
      let cats = obj.Category.map(c => c.Category);
      criteria.Category.forEach(set =>
        set.forEach(catAnd => {
          console.log(cats, catAnd, cats.includes(catAnd))
          if (cats.includes(catAnd)) catMatch = true;
        })
      )
      if (!catMatch) isMatch = false;
    }



    if (criteria.Subcategory) {
      let catMatch = false
      // prepare the Subcategory array
      let subcats = obj.Subcategory.map(c => c.Subcategory);
      criteria.Subcategory.forEach(set =>
        set.forEach(catAnd => {
          //console.log(subcats, catAnd, subcats.includes(catAnd))
          if (subcats.includes(catAnd)) catMatch = true;
        })
      )
      if (!catMatch) isMatch = false;
    }



    obj.Promotion = obj.Promotion === 0 ? 'false' : 'true';
    if (criteria.Promotion && obj.Promotion != criteria.Promotion) isMatch = false;


    return isMatch;

  })

  return p

}

let filter = {
  Rating: [3, 5],
  Category: [
    ['Fitness']
  ],
  Promotion: 'false'
}

console.log(filterProductsBy(filter, data))


filter = {
  Rating: [3, 5],
  Category: [
    ['Fitness'],
    ['OtherCategory']
  ],
  Promotion: 'false'
}

console.log(filterProductsBy(filter, data))

Upvotes: 1

Related Questions