Generi c фильтрация коллекции с использованием предикатов - PullRequest
0 голосов
/ 05 февраля 2020

У меня возникла хитрая проблема с фильтрацией коллекций в kotlin ...

У меня есть базовый класс, который управляет списком элементов, и я хочу иметь возможность фильтровать список по ключевому слову, чтобы я расширен класс с помощью методов Filterable.

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

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

Вот некоторый код:

data class ProductInfo(): {
    var _name: String
    var name: String
            get() = _name
            set(value) { _name = value }
}

abstract class BaseFirestoreAdapter<T : BaseFirestoreAdapter.DataInterface, VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>(), Filterable
{

    var sourceList: MutableList<ProductInfo> = ArrayList()

    ...

    override fun performFiltering(keyword: CharSequence): FilterResults {

        val keywordRegex = keyword.toString().toRegex(setOf(RegexOption.IGNORE_CASE, RegexOption.LITERAL))

        filteredList = sourceList.filter {
                keywordRegex.containsMatchIn(Normalizer.normalize(it.name, Normalizer.Form.NFD).replace("[^\\p{ASCII}]".toRegex(RegexOption.IGNORE_CASE), ""))
        }

        results.values = filteredList.sortedWith(orderComparator)
        results.count = filteredList.size
    }

    ...
}

Я разработал «базовый класс», чтобы он работал с первым классом, упомянутым выше (фильтрация выполняется с помощью «it.name»), и он работает, но теперь, когда я пытаясь сделать его обобщенным c (T), чтобы использовать его со вторым классом (комментарии), я не могу найти способ сделать это ...

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

У меня сейчас немного идей! lol

У кого-нибудь из вас есть идея?


ОБНОВЛЕНИЕ : Следуя предложению @ Tenfour04, я попытался адаптировать его к своему коду, который передает предикаты фильтрации через метод вместо использования конструктора, но он не компилируется, если я не заменю «ActivyInfo :: comments» на что-то вроде «ActivyInfo :: comments.name», но тогда значение, которое я получаю для «seekProperty (it)» в отладке, равно «name», которое не является значением комментария.

Вот код:

CommentAdapter:

override fun getFilter(): Filter {

        super.setFilter(
                { it.state != ProductState.HIDDEN },
                { ActivyInfo::comments },
                compareBy<ProductInfo> { it.state }.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name })

        return super.getFilter()
}

BaseAdapter:

lateinit var defaultFilterPredicate : (T) -> Boolean
lateinit var searchedProperty : (T) -> CharSequence
lateinit var orderComparator : Comparator<T>

fun setFilter(defaultPredicate: (T) -> Boolean, property: (T) -> CharSequence, comparator: Comparator<T> ) {
    defaultFilterPredicate = defaultPredicate
    searchedProperty = property
    orderComparator = comparator
}

override fun performFiltering(constraint: CharSequence): FilterResults {

        ...

        filteredList = sourceList.filter {
                constraintRegex.containsMatchIn(Normalizer.normalize(searchedProperty(it), Normalizer.Form.NFD).replace("[^\\p{ASCII}]".toRegex(RegexOption.IGNORE_CASE), ""))
        }

        ...

    }

1 Ответ

1 голос
/ 05 февраля 2020

Вы можете передать в конструктор параметр, который определяет свойство как функцию.

abstract class BaseFirestoreAdapter<T : BaseFirestoreAdapter.DataInterface, VH : RecyclerView.ViewHolder>(val filteredProperty: (T) -> CharSequence) : RecyclerView.Adapter<VH>(), Filterable
{

    var sourceList: MutableList<T> = ArrayList()

    // ...

    override fun performFiltering(keyword: CharSequence): FilterResults {

        val keywordRegex = keyword.toString().toRegex(setOf(RegexOption.IGNORE_CASE, RegexOption.LITERAL))

        filteredList = sourceList.filter {
                keywordRegex.containsMatchIn(Normalizer.normalize(filteredProperty(it), Normalizer.Form.NFD).replace("[^\\p{ASCII}]".toRegex(RegexOption.IGNORE_CASE), ""))
        }

        results.values = filteredList.sortedWith(orderComparator)
        results.count = filteredList.size
    }

    ...
}

Внесенные вами изменения добавили параметр конструктора filteredProperty, изменив тип sourceList на T и заменой it.name на filteredProperty(it).

Таким образом, подклассам придется вызывать этот супер-конструктор, передавая свойство следующим образом:

data class SomeData(val comments: String)

class SomeDataAdapter: BaseFirestoreAdapter<SomeData>(SomeData::comments) {
    //...
}

Или, если вы хочу сохранить его обобщенным c:

class SomeDataAdapter(filteredProperty: (T) -> CharSequence): BaseFirestoreAdapter<SomeData>(filteredProperty) //...
...