Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boolean statement for multiple filtering Swift

I'm currently creating an app in Swift 4.2 which I'd like a filtering feature in place which allows the user to select multiple filters.

I have an array of the currently selected filters, for example ["Low", "Unread"]. I also have an array of the objects being filtered. But I'm struggling to figure out how to apply multiple filters to this array, especially due to the objects have children which in turn have properties which are filtered against. For example object array holds bulletin.importance.name which is the property that "Low" would be checked against.

The following code is a boolean returning function which will get the filters to be used on the array of bulletin objects:

return (bulletin.bulletinVersion?.issued == true) && (scopes.contains("All") || (scopes.contains((bulletin.bulletinVersion?.bulletin?.importance?.name)!) ||
        (!scopes.contains(where: {$0 == "Low" || $0 == "Normal" || $0 == "High"}))) && (scopes.contains(bulletin.read(i: bulletin.firstReadDate)) ||
            (!scopes.contains(where: {$0 == "Unread"}))) &&
            (scopes.contains(bulletin.signed(i: bulletin.signDate)) && bulletin.bulletinVersion?.bulletin?.requiresSignature == true) && scopes.contains(bulletin.favourited(i: bulletin.favourite)))

This is my current attempt of the boolean check. I wish for it to be hard set so if the user selects "High" and "Unread" it will only show objects which match both of those filters.

The function is called here, getting the filters and filtering an array of ALL bulletins into which ones should be shown based upon the filters:

currentBulletinArray = bulletinArray.filter({bulletin -> Bool in
    let doesCategoryMatch = getScopeFilters(issued: true, scopes: scope, bulletin: bulletin, signature: true)

    if searchBarIsEmpty(){
        return doesCategoryMatch
    } else {
        return doesCategoryMatch && (bulletin.bulletinVersion?.bulletin?.name?.lowercased().contains(searchText.lowercased()))!
    }
   })

I want to be able to set any combination of filters, where the returning .filter predicate will show only the bulletins which match ALL the filters. So, Unread + Unsigned + High will show all High Importance bulletins where they are unread and unsigned.

like image 396
Lewis Seddon Avatar asked Feb 03 '26 14:02

Lewis Seddon


1 Answers

Instead of trying to shove a huge amount of boolean combinations into one statement, I decided to place each filter into a dictionary:

 public var filters:[String: Any] = ["importance":[],
                             "unread": false,
                             "unsigned": false,
                             "favourite": false]

The importance filter will hold an array of strings, for example ["Low", "Medium", "High"].

When the filter buttons are clicked the filters will be toggled if they are a bool or appended/removed from the array:

if(!importanceToAdd.isEmpty){
            filters["importance"] = importanceToAdd
        }
        if(cell.filterTitleLabel.text == "Unread")
        {
            filters["unread"] = true
        }
        if(cell.filterTitleLabel.text == "Unsigned")
        {
            filters["unsigned"] = true
        }
        if(cell.filterTitleLabel.text == "Favourites")
        {
            filters["favourite"] = true
        }

Then, in a seperate function, I check to see if the filters are set, independently of one another. If so, filter the array fo bulletins by each of these conditions:

 if let importance = filters["importance"] as! [String]?{
        if(importance.count != 0){
            filteredBulletins = filteredBulletins.filter({importance.contains(($0.bulletinVersion?.bulletin?.importance?.name)!)})
        }
    }

    if let unread = filters["unread"] as! Bool?
    {
        if(unread)
        {
            filteredBulletins = filteredBulletins.filter({$0.firstReadDate == nil})
        }

    }

    if let unsigned = filters["unsigned"] as! Bool?
    {
        if(unsigned)
        {
            filteredBulletins = filteredBulletins.filter({$0.bulletinVersion?.bulletin?.requiresSignature == true && $0.signDate == nil})
        }
    }

    if let favourite = filters["favourite"] as! Bool?
    {
        if(favourite)
        {
            filteredBulletins = filteredBulletins.filter({$0.favourite == true})
        }
    }

The inclusion and removal of filters to a dictionary really made my needs more clear. Trying to create a monster of a boolean statement would have been infinitely difficult to be dynamic enough to match each possible combination of filters (I'm not even sure it would have been possible).

But thank you to all who commented to offer alternative solutions, you really helped me think outside of the box! :)

like image 199
Lewis Seddon Avatar answered Feb 06 '26 05:02

Lewis Seddon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!