could someone help, please?
I have these functions
fun getBooks(): Single<List<Book>> {
return getCollections()
.map {
it.map(::collectonToBook)
}
}
fun getCollections(): Single<List<Collection>> {
return db.fetchCollections()
.filter(::isBook)
}
fun collectonToBook(collection: Collection): Maybe<Book> {
return collection.toBook()
}
The problem is getBooks returns Single<List<Maybe<Book>>> when I need Single<List<Book>>. Can I do that inside the stream without calling blockingGet?
Try this:
getCollections() // Single<List<Collection>>
.flattenAsFlowable { it } // Flowable<Collection>
.concatMapMaybe { collectonToBook(it) } // Flowable<Book>
.toList() // Single<List<Book>>
In words, unwrap the inner List into its elements, transform the Collection into a Book, concatenate their respective Maybe sources, then finally collect the Books into a List again.
Related
I have two functions, the first one that returns a Result with a model, and the second one that returns a Result with another model.
fun flow1(): Flow<Result<Model1>>
fun flow2(id: String): Flow<Result<Model2>>
What i want is went the flow1() call is success, then do the flow2() call and some logic when is success but at the end return the flow1() result.
And for the moment i just trying something like this:
flow1().flatMapLatest { flow1Result ->
flow1Result.onSuccess {
flow2(it.id).map { flow2Result ->
flow2Result.onSuccess {
//Some logic
}
}
}.onFailure {
// return error
}
}
I have two problems the first one that inside the flatMapLatest give an error because say that i return a Result instead of a Flow. And how i can return the Flow1 result?
Thank you!
Trying something similar to this response Chain kotlin flows depends on Result state
I guess you need something like this
fun main() {
flow1().flatMapLatest { flow1Result ->
// should return flow with flow1Result element emited
flow1Result
.onSuccess {
// flow<flow1Result>, that we return
flow2(it.id).map { flow2Result ->
flow2Result
.onSuccess{
TODO("some logic")
}.onFailure{
// only emit flow1Result when flow2Result = Success
throw RuntimeError()
}
// convert flow<flow2Result> to flow <flow1Result>
flow1Result
}
}
.onFailure {
// there can be other flow<flow1Result>
throw RuntimeError()
}
}
}
I would like to feature toggle a map function on a list. I have a map that I would like to run only if the feature is on:
So for something like this:
items
.map { doTransformation(it) }
.map { runOnlyIfFeatureIsOn(it) }
Is there a way of adding the whole .map function conditionally in kotlin, so that it is only there if it is feature toggled?
let() is handy for doing arbitrary processing in a pipeline, e.g.:
items
.map{ doTransformation(it) }
.let{ if (someCondition) it.map{ runOnlyIfFeatureIsOn(it) } else it }
(For complex/costly conditions, this will be more efficient than putting the if inside the map call, as this'll only evaluate the condition once.)
Maybe just do if in map? There is no problem with that:
val list = listOf(1, 2, 3)
list
.map { it * 2 }
.map {
if (featureIsOn) {
runFeatureMapping(it)
} else {
it
}
}
Using sequences:
var sequence = items.asSequence()
.map { doTransformation(it) }
if (<feature_1_enabled>) {
sequence = sequence.map { runOnlyIfFeature1IsOn(it) }
}
if (<feature_2_enabled>) {
sequence = sequence.map { runOnlyIfFeature2IsOn(it) }
}
val result = sequence.toList()
Sequences are lazy-evaluated and should be used when mutliple operations (filter/map/etc) are applied
How to change the parameters with retry() in kotlin and webflux ?
There is a productInfo function, the function parameter is a collection of product ids.
When I input a wrong id in the list collection ids, the upstream interface will only return the wrong id. And get failed.
What I want to achieve is when the upstream interface returns the wrong id. The product info can remove the wrong id and have a second try with the right ids.
I tried to use retry() but I don't know how to change the parameters in the second try.
fun productInfo(ids: List<Pair<String, String>>): Flux<ProductItem> {
return productWebClient
.get()
.uri("product/items/${ids.joinToString(";") { "${it.second},${it.first}" }}")
.retrieve()
.bodyToFlux(ProductItem::class.java)
.onErrorResume {
logger.error("Fetch products failed." + it.message)
Mono.empty()
}
}
What you want is not retry(). I've built a solution making minor assumptions here and there. You can refer to this solution and make changes according to your requirements. I've used recursion here (productInfo()). You can replace the recursion call with webclient call if the error occurs only once.
fun productInfo(ids: List<Pair<String, String>>): Flux<ProductItem> {
val idsString = ids.joinToString(";") { "${it.second},${it.first}" }
return webClient
.get()
.uri("product/items/${idsString}")
.exchange()
.flatMapMany { response ->
if (response.statusCode().isError) {
response.body { clientHttpResponse, _ ->
clientHttpResponse.body.cast(String::class.java).collectList()
.flatMapMany<ProductItem> { eids ->
val ids2 = ids.filter { eids.contains("${it.second},${it.first}") }
productInfo(ids2)
}
}
} else {
response.bodyToFlux(ProductItem::class.java)
}
}
}
I am trying to convert this RxJava/RxAndroid lesson to RxKotlin/RxAndroid.
At the method at Example5 I get error from picture
My getNotesObservable() function is:
fun getNotesObservable(): Observable<Note>{
val notes: List<Note> = prepareNotes()
return Observable.create {
for (note in notes) {
if (!it.isDisposed){ // onNext only if observable is not disposed
it.onNext(note)
}
}
if (!it.isDisposed) {
it.onComplete()
}
}
}
and part with error is:
disposable.add(
getNotesObservable().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map{
it.note = it.note.toUpperCase()
}
.subscribeWith(getNotesObserver())
)
So, what's should I change in my code?
All that I needed was return statement (from picture below)
Thanks for answer
I'm trying to obtain one document from collection. Document might not exist, and in case of null I wan to return default value.
My query and transformation:
return template.findOne(Query().addCriteria(Criteria.where("id")), DeviceSettings::class.java)
.map {
when (it) {
null -> {
defaultSettings(clock)
}
else -> {
listOf(
Instant.now(clock).toString(),
it.nextMeasurement.toString(),
it.shouldUpdateFirmware.toString()
)
}
}
}
}
Unfortunately above map transformation does not get called.
When I simplify call to simple callable it gets called:
return Mono.fromCallable({
defaultSettings(clock)
})
Reactor does not use nulls in streams and fineOne should return empty mono when there is no result. In your case you should use
switchIfEmpty operator
template.findOne(...)
.map { listOf(...) }
.switchIfEmpty(Mono.just(defaultSettings(clock))