在 Flow 中的 onEach 中过滤集合不起作用
Filtering collection inside onEach in Flow doesn't work
在特定用例中,我正在调用存储库以获取 Flow 形式的数据。
它的类型是:
Flow<Resource<List<Location>>>
其中:
资源是包装器class:
sealed class Resource<T>(val data: T? = null, val message: String? = null) {
class Loading<T>(data: T? = null): Resource<T>(data)
class Success<T>(data: T?): Resource<T>(data)
class Error<T>(message: String, data: T? = null): Resource<T>(data, message)}
位置是我的数据模型class
每个位置都有自己的 属性 类型。当用户切换到酒店类型的部分时,触发用例方法,api 进行调用,我正在过滤列表,以便它只包含所需的项目。
但是问题是过滤机制不起作用。
return repository.getLocations()
.onEach { result ->
if (result.data != null) {
when (locationType) {
is LocationType.All -> result.data
is LocationType.Hotel -> result.data.filter { it.type == "Hotel" }
is LocationType.Explore -> result.data.filter { it.type == "Explore" }
is LocationType.Active -> result.data.filter { it.type == "Active" }
is LocationType.Restaurant -> result.data.filter { it.type == "Restaurant" }
}
}
}
尽管使用onEach
进行过滤,但最终列表没有改变
更新
存储库调用的 return 类型是:
Flow<Resource<List<Location>>>
解决方案
我终于想出了解决办法。
在我的用例中,我正在收集流程并在 collect
lambda 内部,我已经放置了负责过滤的代码。
当一切都完成后,我只是进一步发射数据 ;)
operator fun invoke(
locationType: LocationType
) = flow {
repository.getLocations().collect { result ->
if (result.data != null) {
when (locationType) {
is LocationType.All -> result.data
is LocationType.Hotel -> result.data.filter { it.type == "Hotel" }
is LocationType.Explore -> result.data.filter { it.type == "Explore" }
is LocationType.Active -> result.data.filter { it.type == "Active" }
is LocationType.Restaurant -> result.data.filter { it.type == "Restaurant" }.also { res ->
when (result) {
is Resource.Success -> {
emit(Resource.Success(data = res)) }
is Resource.Loading -> {
emit(Resource.Loading(data = res))
}
is Resource.Error -> {
emit(Resource.Error(data = res, message = result.message ?: ""))
}
}
}
如果你想过滤结果你应该直接过滤它。这里不需要 onEach
。
你可以这样做。
val result = repository.getLocations()
return if(result.data!=null){
result.data.filter { item ->
when (locationType) {
is LocationType.Hotel -> item.type == "Hotel"
is LocationType.Explore -> item.type == "Explore"
is LocationType.Active -> item.type == "Active"
is LocationType.Restaurant ->item.type == "Restaurant"
else -> true
}
}
}else{
emptyList()
}
这只是为了解释,您可以修改并使其更多kotlinify
。
LocationType
在这里是一个常数,所以你可以直接做这样的事情你不需要 when
在这里。
val result = repository.getLocations()
return if(result.data!=null){
result.data.filter { item ->
item.type == locationType
}
}else{
emptyList()
}
要 return 流,您可以 return 如下所示。
result.map { it.data.filter { item -> item.type == locationType } }
filter
不会就地过滤列表。它 return 是列表的过滤 副本 。
在 Flow 上使用 onEach
也没有意义。这只是创建一个新流程,它将在收集每个发出的项目时对其执行 onEach
操作。由于您是从该函数中 returning 的,也许您只需要 Flow 中的第一项,在这种情况下,您应该对其使用 first()
函数,然后使用 returned值。
您需要创建一次过滤副本。然后你可以把过滤后的副本放回一个新的 Success
实例到 return.
val result repository.getLocations().first()
if (result !is Success<List<Location>>) {
return result
}
val filteredData = result.data?.filter {
it.type == when (locationType) {
is LocationType.All -> it.type
is LocationType.Hotel -> "Hotel"
is LocationType.Explore -> "Explore"
is LocationType.Active -> "Active"
is LocationType.Restaurant -> "Restaurant"
}
}
return Success(data = filteredData)
旁注,您忽略了使用密封 class 的要点。由于您将所有可能的属性都放在 parent class 中,因此将其密封并赋予它 children 是没有意义的——它可以再有一个 属性指示它代表加载、成功或错误。现在您必须处理可空 data
和错误 message
,即使您已经检查了 child 类型。使用密封 class 与单个 class 的全部意义在于避免必须使这些可为空。您的 parent class 不应定义任何属性。 Loading class 不需要属性,因此可以是 object
。您的成功 class 可以有一个 non-nullable data
属性,而错误 class 可以有一个 non-nullable message
属性。 Success 和 Error classes 可以是 data
classes 以便更容易比较它们。它应该是这样的:
sealed class Resource<T> {
object Loading<T>: Resource<T>()
data class Success<T>(val data: T): Resource<T>()
data class Error<T>(message: String): Resource<T>()
}
在特定用例中,我正在调用存储库以获取 Flow 形式的数据。
它的类型是:
Flow<Resource<List<Location>>>
其中:
资源是包装器class:
sealed class Resource<T>(val data: T? = null, val message: String? = null) { class Loading<T>(data: T? = null): Resource<T>(data) class Success<T>(data: T?): Resource<T>(data) class Error<T>(message: String, data: T? = null): Resource<T>(data, message)}
位置是我的数据模型class
每个位置都有自己的 属性 类型。当用户切换到酒店类型的部分时,触发用例方法,api 进行调用,我正在过滤列表,以便它只包含所需的项目。
但是问题是过滤机制不起作用。
return repository.getLocations()
.onEach { result ->
if (result.data != null) {
when (locationType) {
is LocationType.All -> result.data
is LocationType.Hotel -> result.data.filter { it.type == "Hotel" }
is LocationType.Explore -> result.data.filter { it.type == "Explore" }
is LocationType.Active -> result.data.filter { it.type == "Active" }
is LocationType.Restaurant -> result.data.filter { it.type == "Restaurant" }
}
}
}
尽管使用onEach
更新
存储库调用的 return 类型是:
Flow<Resource<List<Location>>>
解决方案
我终于想出了解决办法。
在我的用例中,我正在收集流程并在 collect
lambda 内部,我已经放置了负责过滤的代码。
当一切都完成后,我只是进一步发射数据 ;)
operator fun invoke(
locationType: LocationType
) = flow {
repository.getLocations().collect { result ->
if (result.data != null) {
when (locationType) {
is LocationType.All -> result.data
is LocationType.Hotel -> result.data.filter { it.type == "Hotel" }
is LocationType.Explore -> result.data.filter { it.type == "Explore" }
is LocationType.Active -> result.data.filter { it.type == "Active" }
is LocationType.Restaurant -> result.data.filter { it.type == "Restaurant" }.also { res ->
when (result) {
is Resource.Success -> {
emit(Resource.Success(data = res)) }
is Resource.Loading -> {
emit(Resource.Loading(data = res))
}
is Resource.Error -> {
emit(Resource.Error(data = res, message = result.message ?: ""))
}
}
}
如果你想过滤结果你应该直接过滤它。这里不需要 onEach
。
你可以这样做。
val result = repository.getLocations()
return if(result.data!=null){
result.data.filter { item ->
when (locationType) {
is LocationType.Hotel -> item.type == "Hotel"
is LocationType.Explore -> item.type == "Explore"
is LocationType.Active -> item.type == "Active"
is LocationType.Restaurant ->item.type == "Restaurant"
else -> true
}
}
}else{
emptyList()
}
这只是为了解释,您可以修改并使其更多kotlinify
。
LocationType
在这里是一个常数,所以你可以直接做这样的事情你不需要 when
在这里。
val result = repository.getLocations()
return if(result.data!=null){
result.data.filter { item ->
item.type == locationType
}
}else{
emptyList()
}
要 return 流,您可以 return 如下所示。
result.map { it.data.filter { item -> item.type == locationType } }
filter
不会就地过滤列表。它 return 是列表的过滤 副本 。
在 Flow 上使用 onEach
也没有意义。这只是创建一个新流程,它将在收集每个发出的项目时对其执行 onEach
操作。由于您是从该函数中 returning 的,也许您只需要 Flow 中的第一项,在这种情况下,您应该对其使用 first()
函数,然后使用 returned值。
您需要创建一次过滤副本。然后你可以把过滤后的副本放回一个新的 Success
实例到 return.
val result repository.getLocations().first()
if (result !is Success<List<Location>>) {
return result
}
val filteredData = result.data?.filter {
it.type == when (locationType) {
is LocationType.All -> it.type
is LocationType.Hotel -> "Hotel"
is LocationType.Explore -> "Explore"
is LocationType.Active -> "Active"
is LocationType.Restaurant -> "Restaurant"
}
}
return Success(data = filteredData)
旁注,您忽略了使用密封 class 的要点。由于您将所有可能的属性都放在 parent class 中,因此将其密封并赋予它 children 是没有意义的——它可以再有一个 属性指示它代表加载、成功或错误。现在您必须处理可空 data
和错误 message
,即使您已经检查了 child 类型。使用密封 class 与单个 class 的全部意义在于避免必须使这些可为空。您的 parent class 不应定义任何属性。 Loading class 不需要属性,因此可以是 object
。您的成功 class 可以有一个 non-nullable data
属性,而错误 class 可以有一个 non-nullable message
属性。 Success 和 Error classes 可以是 data
classes 以便更容易比较它们。它应该是这样的:
sealed class Resource<T> {
object Loading<T>: Resource<T>()
data class Success<T>(val data: T): Resource<T>()
data class Error<T>(message: String): Resource<T>()
}