Поиск элемента в массиве с максимальным значением

На экране 4 CheckBox`а (сила, выносливость, разум, скорость). В RecyclerView показываем список объектов у которых есть все эти параметры. Если чекнуть CheckBox то из этого списка нужно найти элемент с максимальным значением выбранного параметра и показать этот элемент.

Если чекнуть сразу все CheckBox то выбор максимального по всем параметрам будет..

Есть ViewModel в который отправляются event`ы с состоянием чекбоксов при смене их состояния, в этом ViewModel есть кэш отображаемого списка.

Посоветуйте, пожалуйста, как лучше сделать поиск элемента с максимальными значениями.

Мне на ум ничего кроме этого не приходит. Функция вызывается каждый раз при смене состояния какого либо чекбокса и находит максимальное по полям для которых чекбокс включен (true)

fun search(elements : List<Data>, powerIsChecked: Boolean, enduranceIsChecked: Boolean, speedIsChecked: Boolean) {
    val maxElement = if (powerIsChecked && enduranceIsChecked && mindIsChecked && speedIsChecked) elements.maxBy {
        //если чекнуты все чекбоксы ищем по сумме всех свойств
        it.power + it.endurance + it.mind + it.speed
    } else if (powerIsChecked && enduranceIsChecked ) elements.maxBy {
       //иначе по сумме двух других и т.д. портянка из if else
        it.power + it.endurance
    } else if (powerIsChecked) elements.maxBy {
        it.power
    } else ...
}

Но мне либо в функции search нужно портянку из if else делать, либо в рантайме какой то условно придикат каждый раз инициализировать(разные классы с разной логикой поиска).. Вот никак не пойму как сделать адекватнее


Ответы (1 шт):

Автор решения: Sergei Buvaka

Я не совсем понимаю как можно найти элемент с максимальными 4-мя параметрами? Тут надо определиться.

  • вам нужно к примеру максимальное значение суммы всех параметров?

  • вам нужно брать некий приорите. Например сперва ищем объекты с макс выносливостью, если таких более 1ого, то дальше фильтруем по макс силе и т.д.

Но в любом случае я написал бы свой интерфейс что-то вроде такого:

interface Predicate<T> {

   apply(input : T?) : Boolean
}

И написал бы функцию:

fun search(predicate : Predicate) : YouObject {
    return elements.maxBy { predicate.apply() }
}

Таким образом вы можете на каждый ваш кейс создать класс реализующий интерфейс Predicate и просто передавать его в функцию. Вы инкапсулируете логику поиска предиката и вам не придется писать безмерно большие if else и добавлять туда новый if else всякий раз когда будет добавляться новый кейс.

Тут можно пойти дальше и к примеру в функцию search передавать список предикатов и проверять по всему списку. Тогда вам по сути придется создать всего 4 класса с предикатами, а в месте вызова функции просто добавлять нужную комбинацию. Это будет даже лучше. Но ща вот сходу не напишу такую функцию, но если сами не сообразите, то отпишитесь, я дам вам пример.

UPD Вот пример из реального проекта.

Один из предикатов:

public class AccountTypeFilter implements Predicate<Account> {

    private final AccountType[] types;

    public AccountTypeFilter(AccountType... types) {
        this.types = types;
    }

    @Override
    public boolean apply(Account input) {
        for (AccountType type : types) {
            if (input.getAccountType().equals(type)) {
                return true;
            }
        }
        return false;
    }
}

Вот пример как эти предикаты добавляются в список:

GetAccountsParams(Builder builder) {

        predicates.addAll(builder.predicates);
        if (builder.accountType != null) {
            predicates.add(new AccountTypeFilter(builder.accountType));
        }
        if (builder.hasCard != null) {
            predicates.add(new AccountWithCardFilter(builder.hasCard));
        }
        if (!builder.withHidden) {
            predicates.add(new HiddenAccountFilter());
        }
    }

И потом собственно проверка:

private class AndPredicate<T> constructor(
        private List<Predicate<T>> components;
    ) implements Predicate<T>, Serializable {

        public boolean apply(@Nullable T t) {
            for (Int i = 0; i < components.size(); i++) {
                if (!components.get(i).apply(t)) {
                    return false
                }
            }
            return true
        }
    }

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

→ Ссылка