【问题标题】:Filter a list of object过滤对象列表
【发布时间】:2022-01-12 02:03:35
【问题描述】:

假设我有一个对象列表:

data class Home(
    var id: String = "",
    var status: MutableMap<String, Double> = mutableMapOf(),
    var userName: String = "",
) 

var myList = listOf(
            Home("1", mutableMapOf("hello", 1.0), "firstUser"), 
            Home("2", mutableMapOf("hello", 2.0), "secondUser"), 
            Home("3", mutableMapOf("goodbye", 3.0), "thirdUser")
)

有了这个列表,我想实现一个搜索视图,我想根据地图键或用户名过滤这个列表。例如,如果我搜索“hello”,它将返回一个包含 2 个对象的列表:

listOf(Home("1", mutableMapOf("hello", 1.0), "firstUser"), 
       Home("2", mutableMapOf("hello", 2.0), "secondUser"))

我正在尝试以下代码但没有成功:

searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
        override fun onQueryTextSubmit(query: String): Boolean {
            return false
        }

        override fun onQueryTextChange(newText: String): Boolean {

            myList.stream()
                .filter { p ->
                       p.userName.lowercase(Locale.getDefault())
                                .contains(newText.lowercase(Locale.getDefault())) ||
                       p.status.keys.contains(newText.lowercase(Locale.getDefault()))
                }
                .collect(Collectors.toList()) 

             adapter.submitList(myList)
        }
    })

请对我做错的地方提出任何建议吗?

编辑:正如@Joffrey 所说,我修改了我的代码,以便可以使用 Kotlin 过滤器。我正在按用户名过滤,没关系。当我尝试通过 map 键过滤 myList 时,我必须插入整个文本才能获得任何结果,例如:“hello”。此时,如果地图的键包含子字符串,我需要过滤列表。

override fun onQueryTextChange(newText: String): Boolean {

            val filteredList: List<Home> =
                myList.filter {
                    it.userName.lowercase(Locale.getDefault()).contains(newText.lowercase(Locale.getDefault())) ||
                            it.status.keys.contains(newText)
                }
            adapter.submitList(filteredList)

            return false
        }

【问题讨论】:

  • 你通常应该避免在 Kotlin 中使用 Java 流,在 stdlib 中有大量关于集合的函数使得 stream+op+collect 变得多余(你可以直接使用操作)。也就是说,Java 流和 Kotlin 的过滤器都无济于事,因为它们会返回新集合并且不会影响原始集合。基本上,您当前的 onQueryTextChange 实现会创建一个新列表,并且不会对它做任何事情。
  • 我认为通常您这样做的方式是保留所有数据的原始“完整”列表,然后有一个单独的“过滤”变量,即当前显示的数据。因此,您可以使用 filter 的结果重新分配 filtered 列表,然后以某种方式通知 UI 它需要更新
  • @Joffrey 我刚刚更新了我的代码。使用过滤后的新列表,我更新了回收站视图
  • @Erjon 您仍然没有使用新列表。 myList 未修改,流链末尾的 collect() 调用返回一个新列表。您需要获取此表达式的结果才能获取新列表。如val filteredList = myList.stream().filter{...}.collect(...),或者如我之前所说的使用 Kotlin 的过滤器:val filteredList = myList.filter{...}
  • @Joffrey 好的,我按照你说的修改了代码。如果地图包含子字符串,我该如何过滤它?例如,如果我在搜索视图中输入文本“hel”,它应该返回一个包含 2 个对象的过滤列表,因为它会发现列表中有 2 个具有键“hello”的映射。我说清楚了吗?

标签: list kotlin search hashmap searchview


【解决方案1】:

我已经这样解决了,下面的函数将在列表中找到每个映射,其键包含所需的子字符串:

    infix fun <V> Map<String, V>.withKeyPart(keyPart: String): List<V> {
        return this.filter { it.key.lowercase(Locale.getDefault()).contains(keyPart) }
        .map { it.value }
}

然后应用我这样做的搜索视图:

override fun onQueryTextChange(newText: String): Boolean {

            val filteredList: List<Home> =
                myList.filter {
                    it.userName.lowercase(Locale.getDefault()).contains(newText.lowercase(Locale.getDefault())) ||
                            it.status.withKeyPart(newText.lowercase(Locale.getDefault())).isNotEmpty()
                }

            adapter.myList(filteredList)

            return false
        }

【讨论】:

    猜你喜欢
    • 2017-06-14
    • 2015-03-24
    • 2011-01-10
    • 2013-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多