I am dealing with a situation where I receive a general object in my activity that has many relations with other objects from my room database. I need to access a low level object and have to iterate multiple times with forEach, like:
items.forEach{ item ->
item.categories.forEach{ category ->
category.dataTypes.forEach{ dataType ->
dataType.configValues.forEach{ value ->
if (value) (...)
}
}
}
}
It feels pretty annoying to have such a chain of statements like this. It looks like bad code somehow. Is there a way to reduce this boilerplate in Kotlin?
How about flat mapping before?
items.flatMap { it.categories }
.flatMap { it.dataTypes }
.flatMap { it.configValues }
.filter { ... your filter ... }
.forEach { ... }
The chain is effectively still there, but it is flattened.
Related
I'm building a repository to retrieve data from a Room database. The Room dao returns a Flow<List<ObjectDto>>. However, I need to convert this to Flow<List<Object>>. What is the right way to do this?
This is the solution I've come up with. I have a mapper extension ObjectDto.toObject(). However, this solution doesn't seem right to me. I have no experience with flows, but collecting and emitting again can't be correct, right?
override fun getObjects(): Flow<List<Object>> {
return flow {
objectDao.getObjects().collect { objectDtoList ->
val objects = objectDtoList.map { it.toObject() }
emit(objects) }
}
}
I also found several operators to use on flows without collecting them, but while some of them are able to change the type, I'm not sure how to change the type of a list using these operators.
I think Flow.map is what you're looking for
override fun getObjects(): Flow<List<Object>> =
objectDao.getObjects().map { objectDtoList ->
objectDtoList.map { it.toObject() }
}
}
I'm operating on very large Kotlin sequence, I'm executing my logic on every step of the sequence and I never need to keep the whole sequence in memory.
Currently my code looks like this
hugeSequence
.filter { ... }
.map {...... }
.onEach {
callExpensiveOperation(it)
}
.toList() <- this feels wrong
The toList() at the bottom is the terminal operator, but I'm worried that Kotlin may try to create a huge list in memory, before realising that I'm not even assign the result value of that operation.
Is there any other terminal operator I can use just to trigger the sequence to start?
Use forEach instead of onEach. It is the terminal equivalent of onEach.
hugeSequence
.filter { ... }
.map {...... }
.forEach {
callExpensiveOperation(it)
}
Let's consider this function
#Transactional
fun conditionalInsertEntity(dbEntity: DBEntity): Mono<DBEntity> {
return fetchObjectByPublicId(dbEntity.publicId)
.switchIfEmpty {
r2DatabaseClient.insert()
.into(DBEntity::class.java)
.using(Flux.just(dbEntity))
.fetch()
.one()
.map { it["entity_id"] as Long }
.flatMap { fetchObjectById(it) }
}
}
while running above function with following driver code I get duplicate entry errors if the list contains duplicates. Ideally it shouldn't give that error because the above function is already handling the case for duplicate inserts!!
val result = Flux.fromIterable(listOf(dbEntity1, dbEntity1, dbEntity2))
.flatMap { conditionalInsertEntity(it) }
.collectList()
.block()
Realized that this is an issue of using flatMap instead of concatMap.
ConcatMap collects the result from individual publishers sequentially unlike flatMap. (more here)
Because I used flatMap, multiple publishers thought that the entity isn't already available in the DB
Implemented Caching by the following link:
https://blog.mindorks.com/implement-caching-in-android-using-rxjava-operators
fun getSavedAddressList(): Maybe<List<SavedAddress>?>? {
return Observable.concat(
getAddressListMemory(),
getAddressListDatabase(),
getAddressListNetwork()).firstElement()
}
fun getAddressListDatabase(): Observable<List<SavedAddress>?> {
return Observable.create(ObservableOnSubscribe { emitter: ObservableEmitter<List<SavedAddress>?> ->
val list: ArrayList<SavedAddress> = addressDao.getAddressList() as ArrayList<SavedAddress>
if (list.isNotEmpty()) {
emitter.onNext(list)
}
emitter.onComplete()
if (list.isNotEmpty())
getAddressListNetwork().subscribeOn(schedulerProvider.io())?.observeOn(schedulerProvider.io())?.subscribe()
})
}
items in the database are retrieving perfectly after storing into database
problem is network calling is not happening after getting a list from database
I want to get three data source sequentially one after another and store latest data in the database
First of all, you're leaking the getAddressListNetwork Disposable in there because you are trying to do too much inside the getAddressListDatabase.
I think what you want is this:
fun getSavedAddressList(): Observable<List<SavedAddress>> {
return Observable.concat(
getAddressListMemory(),
getAddressListDatabase(),
getAddressListNetwork()).distinctUntilChanged())
}
This will always try to fetch the addresses from the 3 sources, and only emitting if the data is different than the previous emission, meaning the data is "fresher".
To be honest with you, I think you need to have a look at the concept of "stale data" and "cache invalidation".
I am fairly new in RxJava pradigm. I am doing following is leading to nested Single objects.
tickHappened.map{
func(it)
}
//I get Single<Single<ArrayList<String>>>
Here tickHappened:Single<T> and func<T>(T param):Single<ArrayList<String>>
tickHappened.map{
func(it)
}
//I get Single<Single<ArrayList<String>>>
.map { single ->
single.map { list ->
list.size
}
}
I actually need to return Single<Int> which is the size of the Arraylist passed. I need to use map twice in the above chain which leads to Single<Single<Int>>
Is there a way to avoid nesting Singles? If I understand Rxjava, it doesn't make sense to have a Single which enclose another Single? If not, then is there a way to return Single<Int>?
As a beginner, one thing to learn is the flatMap operator that is available all around RxJava and is the most common operator needed for solving problems:
tickHappened
.flatMap { func(it) }
.map { it.size() }