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() }
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)
}
I have two maps, let's call them oneMap and twoMap.
I am iterating through all the keys in oneMap, and if the key exists in twoMap I do something
like
fun exampleFunc(oneMap: Map<String, Any>, twoMap: Map<String, Any>) {
for((oneMapKey, oneMapVal) in oneMap) {
if (twoMap.containsKey(oneMapKey)) {
val twoMapVal = twoMap[oneMapKey]
if (twoMapVal == oneMapVal) {
//do more stuff
}
//do more stuff, I have more if statements
}
}
}
To avoid having more nested if statements, I was wondering if instead I could get rid of the
if (twoMap.containsKey(oneMapKey)) check. if twoMap doesn't contain the oneMapKey, we get a null object, and my code still works fine. I was wondering if this is considered bad convention though
fun exampleFunc(oneMap: Map<String, Any>, twoMap: Map<String, Any>) {
for((oneMapKey, oneMapVal) in oneMap) {
val twoMapVal = twoMap[oneMapKey]
if (twoMapVal == oneMapVal) {
//do more stuff
}
//do more stuff, I have more if statements
}
}
It depends. Do you wanna execute the "more stuff" or not?
If you do not wanna execute it you should keep the if condition. Though, if you are concerned about indentation (and deep if hierarchies) you can consider breaking out of the loop:
for((oneMapKey, oneMapVal) in oneMap) {
if (!twoMap.contains(oneMapKey)) continue // continue with next iteration
// do more stuff
}
If your map does not contain null values you can also get the value and check if the result was null (which means the key was not present in the map):
for((oneMapKey, oneMapVal) in oneMap) {
val twoMapVal: Any = twoMap[oneMapKey] ?: continue // continue with next iteration
// do more stuff
}
So its always good practice the remove useless code and (in my opinion) to have less if-hierarchies, as you can easily loose focus when you have lots of nested conditions.
As Tenfour04 says, omitting the containsKey() check is only an option if the map values aren't nullable; if they are, then []/get() gives no way to distinguish between a missing mapping and a mapping to a null value.
But if not (or if you want to ignore null values anyway), then I'd certainly consider omitting the check; the resulting code would be slightly shorter and slightly more efficient, without losing clarity or maintainability. It could also avoid a potential race condition. (Though in a multi-threaded situation, I'd be considering more robust protection!)
One variation is to use let() along with the safe-call ?. operator to restrict it to non-null cases:
for ((oneMapKey, oneMapVal) in oneMap) {
twoMap[oneMapKey]?.let { twoMapVal ->
if (twoMapVal == oneMapVal) {
// Do more stuff
}
// Do more stuff
}
}
Using ?.let() this way seems to be a fairly common idiom in Kotlin, so it should be fairly transparent.
Is there a way to sort a collection emitted by a flow in a custom order like:
fun getList():Flow<Something>
fun main(){
launch{
getList().filter{}.map{}.sortBy{
//
}.toList()
}
}
You can toList() first and then sortBy(). Sorting a flow does not always make sense because a flow, by definition, does not know if there are going to be any more elements in the stream.
You can apply some actions like that:
getList().transform {
//it - list
// sortedList - some function to perform sorting or something else
emit(sortedList(it))
}
UPD: You can use map(similar to "transform", but more simple) and filter(it's used to emit only specific values of the flow) functions as well to perform some actions. "transform" function allows you to perform more specific actions. In that case they are same.
getList().map {
sortedList(it)
}
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.