Логическое выражение для множественной фильтрации Swift - PullRequest
0 голосов
/ 16 октября 2018

В настоящее время я создаю приложение в Swift 4.2, в котором я хотел бы использовать функцию фильтрации, которая позволяет пользователю выбирать несколько фильтров.

У меня есть массив выбранных в данный момент фильтров, например["Низкий", "Непрочитанный"].У меня также есть массив объектов, которые фильтруются.Но я изо всех сил пытаюсь выяснить, как применить несколько фильтров к этому массиву, особенно из-за того, что у объектов есть дочерние элементы, которые, в свою очередь, имеют свойства, которые фильтруются.Например, в массиве объектов содержится bulletin.importance.name, свойство, с которым будет проверяться значение «Low».

Следующий код представляет собой логическую функцию возврата, которая получит фильтры, которые будут использоваться в массиве бюллетеня.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)))

Это моя текущая попытка логической проверки.Я хочу, чтобы он был жестко установлен, поэтому, если пользователь выберет «Высокий» и «Непрочитанный», он будет показывать только объекты, которые соответствуют обоим этим фильтрам.

Функция вызывается здесь, получая фильтры и фильтруямассив ВСЕХ бюллетеней, в которые должны быть показаны, основываясь на фильтрах:

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()))!
    }
   })

Я хочу иметь возможность установить любую комбинацию фильтров, где возвращаемый предикат .filter будет показывать только бюллетени, которые соответствуют ВСЕМфильтры.Таким образом, непрочитанные + неподписанные + высокие будут показывать все бюллетени высокой важности там, где они не прочитаны и не подписаны.

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Вместо того, чтобы пытаться объединить огромное количество логических комбинаций в один оператор, я решил поместить каждый фильтр в словарь:

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

Фильтр важности будет содержать массив строк, например [«Низкий», «Средний», «Высокий»].

При нажатии кнопок фильтров фильтры будут переключаться, если они являются логическими переменными или добавляются / удаляются из массива:

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
        }

Затем в отдельной функции я проверяю, установлены ли фильтры независимо друг от друга.Если это так, отфильтруйте массив бюллетеней по каждому из следующих условий:

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

Включение и удаление фильтров в словаре действительно сделало мои потребности более понятными.Попытка создать монстра из логического выражения была бы бесконечно трудной, чтобы быть достаточно динамичной, чтобы соответствовать каждой возможной комбинации фильтров (я даже не уверен, что это было бы возможно).

Но спасибо всемКто прокомментировал предложение альтернативных решений, вы действительно помогли мне мыслить нестандартно!:)

0 голосов
/ 16 октября 2018

Я не уверен, что вы спрашиваете, но здесь идет.Сначала вы могли бы отформатировать свой код, чтобы сделать его более читабельным ... Я знаю, что множественные точки возврата считались плохими, но, похоже, ситуация немного изменилась.Я выбрал бы случаи в порядке важности и вернул бы любые определенные логические значения, которые вы можете найти.Например (непроверенный код, поскольку я не знаю, что такое Bulletin и Scopes):

func foo(bulletin: Bulletin, scopes: Scopes) -> Bool {
    if bulletin.bulletinVersion?.issued == true && scopes.contains("All") {
        return true
    }
    if scopes.contains(bulletin.bulletinVersion?.bulletin?.importance?.name)! {
        return true
    }
    if scopes.contains(bulletin.bulletinVersion?.bulletin?.importance?.name)! {
        return true
    }
    if !scopes.contains(where: {$0 == "Low" || $0 == "Normal" || $0 == "High"})
        && (scopes.contains(bulletin.read(i: bulletin.firstReadDate) {
        return true
    }

    if !scopes.contains(where: {$0 == "Unread"}) 
        && scopes.contains(bulletin.signed(i: bulletin.signDate)
        && bulletin.bulletinVersion?.bulletin?.requiresSignature == true
        && scopes.contains(bulletin.favourited(i: bulletin.favourite)) {
        return true
    }
    return false
}

Обратите внимание, что каждое из этих предложений проверяет значение true и возвращает его, если необходимо, поэтому функция возвращается, как только истинадело найдено.Порядок значений tts мог бы влиять на логику.

Я бы рассмотрел создание протокола с методом, который взял бы бюллетень, область действия и вернул true или false.

protocol Filter {
    func test(bulletin: Bulletin, scopes: Scopes) -> Bool
}

ЗатемСоздание разработчиков этого протокола:

class AFilter: Filter {
    func test(bulletin: Bulletin, scopes: Scopes) -> Bool {
        if bulletin.bulletinVersion?.issued == true && scopes.contains("All") {
            return true
        }
        return false
    }
}

После того, как вы создали список экземпляров фильтра, который вы можете сделать (измененный в соответствии с уточненными требованиями):

for filter in filters {
    if !filter.test(bulletin: bulletin, scopes: scopes) {
        return false
    }
}
return true

(Бонус: обратите внимание, как легкоэто должно было изменить логику с этим шаблоном дизайна)

...