【问题标题】:Generic collection filtering using predicates使用谓词的通用集合过滤
【发布时间】:2020-05-20 21:11:21
【问题描述】:

我遇到了一个关于 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”完成的)并且它可以工作,但现在我正试图使其通用 (T) 使用它与第二类(cmets),我找不到办法......

我以为我可以传递一个与类相关的谓词,定义如何在过滤期间匹配项目,但由于关键字仅在 performFiltering 方法中已知,因此我无法在此方法之外正确创建谓词...

我现在有点想不通了!哈哈

你们有什么想法吗?


更新:按照@Tenfour04 的建议,我尝试将其调整为通过方法而不是使用构造函数传递过滤谓词的代码,但除非我替换“ActivyInfo::cmets”,否则它不会编译使用类似“ActivyInfo::cmets.name”的内容,但随后 我在调试中为“searchedProperty(it)”得到的值是“name”,它不是注释值。

代码如下:

评论适配器:

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

基础适配器:

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

        ...

    }

【问题讨论】:

  • 这与您的问题无关,但您的 ProductInfo 看起来像是一些等待发生的错误。它是一个没有构造函数参数的数据类,因此它的每个实例都将通过equals 与任何其他实例的测试。此外,_namename 属性具有非常不寻常和令人惊讶的行为。
  • 我简化了帖子的代码,数据类比这复杂得多,并且有两个构造函数:-)

标签: java kotlin filter collections predicate


【解决方案1】:

您可以向构造函数传递一个参数,将属性指定为函数。

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) {
    //...
}

或者如果你想保持通用:

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

【讨论】:

  • 我尝试将您的建议调整为我的代码,该代码通过方法而不是使用构造函数传递过滤谓词,但除非我将“ActivyInfo::cmets”替换为“ActivyInfo::cmets”,否则它不会编译.name”,但是我在调​​试中为“searchedProperty(it)”得到的值是“name”,它不是注释值。请参阅我更新的代码问题。
  • 谓词类型是一个函数,输入类型为T,输出类型为CharSequence。您可以通过传递 ActivityInfo::comments 直接传递函数/属性,也可以传递具有参数 T 并返回 CharSequence 的 lambda,例如 { it.comments },但您传递的是返回属性 { ActivityInfo::comments } 的 lambda ,所以它不起作用。
  • 哈!但是当然哈哈我忘记了那些牙套哈哈谢谢,它现在似乎可以正常工作了!
猜你喜欢
  • 2019-03-17
  • 1970-01-01
  • 2014-10-26
  • 1970-01-01
  • 1970-01-01
  • 2010-09-12
  • 2020-07-17
  • 1970-01-01
相关资源
最近更新 更多