1

I have a angular component which has got 3 filters (category, rating & price) and when selecting those filters I have to show only the appropriate data so I have created a Pipe and passed in all three selected values. Actually my category filter works completely fine but the other two filters (rating & price) are not working properly.

Json data:

"allBusiness": [    
        {
            "category": "XYZ",
            "description": "something something",
            "business": [
             {
                "type": "Store",
                "rating": 4,
                "price" : "$"
             },
             {
                "type": "Store",
                "rating": 3,
                "price" : "$"
             },
             {
                "type": "Store",
                "rating": 5,
                "price" : "$$"
             }           
             ]

        },
        {
            "category": "ABC",
            "description": "Testing",
            "business": [
             {
                "type": "Online",
                "rating": 3,
                "price" : "$"
             },
             {
                "type": "Store",
                "rating": 2,
                "price" : "$"
             },
             {
                "type": "Store",
                "rating": 1,
                "price" : "$$"
             }           
             ]

        }
]

FilterPipe.ts:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'filter'
})
export class FilterPipe implements PipeTransform {

  transform(allBusiness: any[], selectedCategory: string, selectedRating: number, selectedPrice: string): any {
    if (allBusiness && allBusiness.length){
        return allBusiness.filter(item =>{
            if (category && item.category.indexOf(selectedCategory) === -1){
                return false;
            }
            if (rate && item.business.filter(rt => rt.rating != selectedRating)){ //tried filter, findIndex, some but nothing works
                return false;
            }
            if (price && item.business.find(pr => pr.price != selectedPrice)){ //same here nothing is working
                return false;
            }
            return true;
       })
    }
    else{
        return items;
    }
  }
}

Thanks in advance.

2 Answers 2

2

We have 2 filters: for outer list and inner list, let me split it to allBusinessFilterBy: {category?: string} and businessFilterBy: {rating?: number; price?: string;}.

interface BusinessFilterBy {
  rating?: number;
  price?: string;
}

interface AllBusinessFilterBy {
  category?: string;
}


function isNill(item) {
  return item === undefined || item === null || (item !== item);
}

function isBusiness(item, filterBy: BusinessFilterBy) {
  return (isNill(filterBy.rating) || item.rating === filterBy.rating) && (isNill(filterBy.price) || item.price === filterBy.price);
}

function isAllBusiness(item, filterBy: AllBusinessFilterBy) {
  return isNill(filterBy.category) || item.category === filterBy.category;
}

function filterBusiness(item, businessFilterBy: BusinessFilterBy) {
  return [{
    ...item,
    business: item.business.filter(businessItem => isBusiness(businessItem, businessFilterBy))
  }];
}

function transform(allBusiness = [], allBusinessFilterBy: AllBusinessFilterBy = {}, businessFilterBy: BusinessFilterBy = {}) {
  if (!allBusiness && !allBusiness.length) {
    return allBusiness;
  }
  return allBusiness.reduce((acc, item) => [...acc, ...isAllBusiness(item, allBusinessFilterBy) ? filterBusiness(item, businessFilterBy) : []], []);
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks a lot for your time but something is missing and its not working. Sometimes its working when I select all 3 values (using dropdown filters). Actually it should work if I select any one of the 3 filter. Also, FYI, I have "Sort by category", "Sort by rating" & "Sort by price" as first option in the dropdown's so those should be excluded while filtering (its like resetting the filters).
Buggy - I have a created a stackblitz. Please refer to this link and help me to fix this issue. stackblitz.com/edit/angular-utavhg
As i understood you want to filter rather than sort ) Used my answer and add to it: - convertNull for Sort by <> case . - filter result to ignore categories without items . stackblitz.com/edit/angular-bcpd7w
Buggy - Thanks a lot... Its working now and sorry for the confusion (filter vs sort). Also, I couldn't understand what you are doing in this line... Can you please explain? allBusiness.reduce((acc, item) => [...acc, ...isAllBusiness(item, allBusinessFilterBy) ? filterBusiness(item, businessFilterBy) : []], []);
It is more clear way to say: allBusiness .filter(item => isAllBusiness(item, allBusinessFilterBy)) .map(item => filterBusiness(item, businessFilterBy));
1

I'm a little unsure what you mean when you say I have to show only the appropriate data, in relation to filtering by rating or price. I assume you want to:

  • Filter/exclude an item in allBusiness if none of the entries in its business array contain a specified rating and price.
  • Output/include an item if it contains, at least one entry in its business array that has the specified rating and price.

If that is correct, you can do this:

return allBusiness.filter(item => 
   (selectedCategory && item.category && item.category.indexOf(selectedCategory) > -1)
   && (selectedRating && item.business && item.business.length > 0 && item.business.find(rt => rt.rating == selectedRating))
   && (selectedPrice && item.business && item.business.length > 0 && item.business.find(pr => pr.price == selectedPrice)))

Now, if you want to filter allBusiness such that you only want an entry where all items in its business array have the specified rating and price, you can do this:

return allBusiness.filter(item => 
   (selectedCategory && item.category && item.category.indexOf(selectedCategory) > -1)
   && (selectedRating && item.business && item.business.length > 0 && !item.business.find(rt => rt.rating != selectedRating))
   && (selectedPrice && item.business && item.business.length > 0 && !item.business.find(pr => pr.price != selectedPrice)))

Note:

find() gets the first item in an array that matches the condition you give it. If no items match the condition, it returns undefined.

filter(), on the other hand, gives you a new array with only items from the original which match the condition you give it. If no items match the condition, it gives you an empty array.

1 Comment

Thanks for your response. Btw, "show only the appropriate data" means, if the user selects a "rating" as 4 then show only items which has rating = 4. Similarly if the user selects a "price" as "$$" then show only items price = "$$". Also, if they select both rating & price then it should show only items which satisfies both (like an AND condition). I tried both the solution you provided and nothing works. Also, FYI, I have "Sort by category", "Sort by rating" & "Sort by price" as first option in the dropdown's so those should be excluded while filtering (its like resetting the filters).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.