Reputation: 728
In particular use case I'm making a repository call to obtain data in form of Flow.
It's type is:
Flow<Resource<List<Location>>>
Where:
Resource is wrapper 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)}
Location is my data model class
Each location has it's own property like type. When user switch to section where type's Hotel, use case method is triggered, api call is made and I'm filtering list so that it contains only desirable items.
However the problem is filtering mechanims which doesn't work.
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" }
}
}
}
Final list isn't changed despite filtering with use ofonEach
UPDATE
The return type of repository call is:
Flow<Resource<List<Location>>>
SOLUTION
Finally I've come up with the solution.
In my use case I'm collecting the flow and inside collect
lambda, I've put code responsible for filtering.
When everything is done I'm just emitting data further ;)
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 ?: ""))
}
}
}
Upvotes: 0
Views: 1236
Reputation: 21053
If you want to filter the result you should filter it directly . the onEach
is not needed here.
You can do it this way .
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()
}
this is just for explanation you can modify and make it more kotlinify
.
LocationType
is a constant here so you can directly do something like this you do not need a when
here.
val result = repository.getLocations()
return if(result.data!=null){
result.data.filter { item ->
item.type == locationType
}
}else{
emptyList()
}
To return a Flow you can return something like below.
result.map { it.data.filter { item -> item.type == locationType } }
Upvotes: 1
Reputation: 93739
filter
doesn't filter a list in place. It returns a filtered copy of the list.
It also doesn't make sense to use onEach
on your Flow. This is only creating a new Flow that will perform the onEach
action on each emitted item when it gets collected. Since you are returning from this function, maybe you just need the first item from the Flow, in which case you should use the first()
function on it and then work with the returned value.
You need to create your filtered copy one time. Then you can put that filtered copy back into a new Success
instance to 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)
Side note, you are missing the point of using a sealed class. Since you are putting all the possible properties in the parent class, there's no point in making it sealed and giving it children--it could just have one more property that indicates it represents loading, success, or error. Now you have to deal with nullable data
and error message
even if you've checked the child type. The whole point of using a sealed class vs. a single class would be to avoid having to make those nullable. Your parent class should have no properties defined. The Loading class doesn't need properties and can therefore be an object
. Your Success class can have a single non-nullable data
property, and the Error class can have a single non-nullable message
property. The Success and Error classes can be data
classes so they are more easily compared. It should look like this:
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>()
}
Upvotes: 1