Learn Courses My Dashboard

Multiple Filters on Foreach

Hello community! I have a Foreach loop containing many objects of a class and I want to give the user the ability, to apply multiple filters to that foreach. Using just one filter criteria is quite doable, but how do I achieve multiple filters? As an example: let’s say the class is called ,Car" and contains a brand name, a color and a price. A Foreach loop contains 30 car objects. In this case, the user could specify that only yellow cars, of the brand Toyota, under 20k$ are shown, or they can apply different filters and what the foreach shows, changes accordingly. My internet research hasn’t been fruitful and I could only think about chained if statements, but that is probably not the way to go. Any hints in the right direction? Thank you!

Use a computed variable to apply your filter conditions to the list of Car objects and return the filtered list to the ForEach for looping through.

So instead of, for instance:

ForEach(listOfCars) { car in 
   //blah blah blah
}

You would do:

ForEach(filteredListOfCars) { car in
    //blah blah blah
}

And in the same View struct you would have a computed property like:

var filteredListOfCars: [Car] {
    //start with the full list of cars
    var carsToShow = listOfCars
    //then do your filtering based on the user's selections
    return carsToShow
}
1 Like

Hello Roosterboy. thank you for your reply!
Unfortunately my problem was the filtering itself. (in your code: //then do your filtering based on the user’s selection)
I tried some things and wonder if the approach shown bellow is going in the right direction.

What I came up with: each search criteria has a variable like :
var chosenColor = ""
Then the user selects a filter criteria and it becomes this:
var chosenColor = "Yellow"
Then the filtering starts:

  private var filteredCars: [Car]{
            // Filter by the search terms and return cars with criteria 
            return cars.filter{r in
//here comes the problem: If for example chosenBrand == "", no cars are shown. The same applies to chosenColor or Price. also: r.color is a String array
                if  r.brand.contains(chosenBrand) && r.color.contains(chosenColor) && r.price == chosenPrice{
               return true
                }
                else
                {return false }
            }
        }
    }

This code does already work, if every filter criteria is assigned a value, but I am struggling with the value → “” or having multiple values. I can only think of very long if blocks to solve this and there must be something better /:

Rather than trying to cram it all into one filter call, I would break it up into multiple conditions, one for each characteristic that can be searched on. You would start with the full list and then apply filters one by one until you get the final list.

(I would also pull the filtering logic into your view model instead of leaving it in the View, but that’s up to you.)

Something like this (obviously not exactly like your model):

var filteredCars: [Car] {
    //start with the full list
    var carsToShow = cars
    
    if !brandFilter.isEmpty {
        carsToShow = carsToShow.filter {
            $0.brandName == brandFilter
        }
    }
    
    if !colorFilter.isEmpty {
        carsToShow = carsToShow.filter {
            $0.color.accessibilityName == colorFilter
        }
    }

    if priceFilter > 0 {
        //note that if priceFilter defaults to 0, you don't even need to check first
        //and can just test $0.price >= priceFilter
        carsToShow = carsToShow.filter {
            $0.price >= priceFilter
        }
    }

    return carsToShow
}

This approach works well for an and-based search, where the results have to match all the selected criteria.

There are other different approaches you can take as well, depending on your use case.

You could, for instance, filter each criteria to its own array, then combine them all at the end in a Set to eliminate duplicates and then return Array(yourSet) to use in the ForEach. This would work well for an or-based search, where the results match any of the criteria but not necessarily all of them.

Or, if your model is a class and you can annotate it with @objcMembers and inherit from NSObject, you can use NSPredicate to filter the full list against complex predicates that use boolean logic and other tricks.

Or you can use keypaths to build a predicate system in Swift that replicates a lot of the functionality of NSPredicate without the Objective-C reliance.

Thank you!! :smiley: This solves the issue really well.