using Rxjava with flatmap not change the downstream data - kotlin

The bounty expires in 3 days. Answers to this question are eligible for a +500 reputation bounty.
ant2009 wants to draw more attention to this question.
RxJava3
Kotlin
I have the following method and I am trying to control the downstream to return the Pair<Customer, Profile> after the map operator
However, after the first flatMap the data is changes to customer. Basically, I want to return the Pair<Customer, Profile> and not the customer dowwstream.
override fun execute(): Single<Pair<Customer, Profile>> {
return Single.fromObservable(
graphQLProfileService.getCustomerProfile()
).map {
Pair(
it.mdc.toCustomer(),
it.profile
)
} /* Single<Pair<Customer, Profile>> */
.flatMap { /* it: Pair<Customer, Profile> */
updateProfileUseCase.execute(topsCustomer = it.first)
}
.flatMap { /* it: Customer */
updateUserUseCase.execute().toSingleDefault(it) /* it.first, as the first item will be customer */
}
.flatMap { /* it: Customer */
updateAppUseCase.execute().toSingleDefault(it) /* it.first */
}
}
I think the solution should be something like this but I haven't managed to get it to work:
https://medium.com/#douglas.iacovelli/rxjava-kotlin-keep-the-original-value-in-a-flatmap-bbd6a6974a99

You have to put those flatMaps inside a flatMap and once they succeed, you have to replace their results back to the value by ignoring it and concatenating with a just of the pair:
Single.fromObservable(
graphQLProfileService.getCustomerProfile()
).map {
Pair(
it.mdc.toCustomer(),
it.profile
)
}
.flatMap { original ->
Single.just(original) // <--------------------------------------
.flatMap { /* it: Pair<Customer, Profile> */
updateProfileUseCase.execute(topsCustomer = it.first)
}
.flatMap { /* it: Customer */
updateUserUseCase.execute().toSingleDefault(it) /* it.first, as the first item will be customer */
}
.flatMap { /* it: Customer */
updateAppUseCase.execute().toSingleDefault(it) /* it.first */
}
.ignoreElement() // <--------------------------------------
.andThen(Single.just(original))
}

Related

How to get object of Maximum value from LiveData?

I have liveData of market data. I want one market data object which have highest 'volume'. Here, volume is string value("277927.5793846733451135"), it could be null also.
I am using below code to achieve this. but, its not working.
viewModel.marketlist.observe(this as LifecycleOwner, { marketdata ->
val marketData = marketdata.getOrNull()
if(marketData !=null) {
val mData: MarketData? = marketData.marketData?.maxByOrNull { checkNotNull(it.volume) }
if (mData != null) {
binding.textViewPrice.text = mData.price
}
}
else {
//TODO
}
})
Any help would be appreciated!
You should be able to do something like this:
viewModel.marketList.observe(viewLifecycleOwner) { marketData ->
val maxData = marketData.getOrNull()?.marketData?.let { dataValues ->
dataValues.maxByOrNull { it.volume?.toDoubleOrNull() ?: -1.0 }
}
if (maxData != null) {
binding.textViewPrice.text = maxData.price
}
}
I cleaned up the observe call a bit, then I'm checking if marketData.getOrNull().marketData is null right away with my let { ... } block.
If you do have marketData (the inner one), it'll then safely call maxByOrNull { it.volume }.

How to fold results from a few async calls

I want to get the results of my asynchronous function and use them in the fold function. Here's my function that didn't work:
private fun sendOrderStatus(list: List<OrderStatusEntity>) : Single<Boolean> {
return Single.just(
list.fold(true) { initial, item ->
if (!item.isTerminal()) {
val info = OrderStateParameters(
lon = item.orderStatusLon!!,
lat = item.orderStatusLat!!,
datetime = item.orderStatusDate!!
)
val state = OrderState(item.orderId, item.toSend(), info)
return tasksUseCase.sendOrderStatusForWorker(state)
.doOnSuccess { markSent(item) } // side calling
.flatMap {
return#flatMap initial && it.isSuccess // that result should be used in *fold*-function
}
} else // stub result
true
}
)
}
So, I intend to return a Single that will contain the aggregated result of all tasksUseCase.sendOrderStatusForWorker(state) calls.
Thank for any helps!

Concurrency, react-ing to more than one supply at a time

Please consider the code below. Why is the output of this is "BABABA" and not "AAABAA" / "AABAAAB"? Shouldn't the two supplies run in parallel and the whenever fire immedeatly when there is an event in any of them?
my $i = 0;
my $supply1 = supply { loop { await Promise.in(3); done if $i++> 5; emit("B"); } };
my $supply2 = supply { loop { await Promise.in(1); done if $i++> 5; emit("A"); } };
react
{
#whenever Supply.merge($supply1, $supply2) -> $x { $x.print }
whenever $supply1 -> $x { $x.print };
whenever $supply2 -> $x { $x.print };
}
When we subscribe to a supply block, the body of that supply block is run immediately in order to set up subscriptions. There's no concurrency introduced as part of this; if we want that, we need to ask for it.
The best solution depends on how close the example is to what you're doing. If it's very close - and you want to emit values every time interval - then the solution is to use Supply.interval instead:
my $i = 0;
my $supply1 = supply { whenever Supply.interval(3, 3) { done if $i++ > 5; emit("B"); } };
my $supply2 = supply { whenever Supply.interval(1, 1) { done if $i++> 5; emit("A"); } };
react {
whenever $supply1 -> $x { $x.print };
whenever $supply2 -> $x { $x.print };
}
Which simply sets up a subscription and gets out of the setup, and so gives the output you want, however you do have a data race on the $i.
The more general pattern is to just do anything that gets the loop happening out of the setup step. For example, we could use an a kept Promise to just "thunk" it:
my constant READY = Promise.kept;
my $i = 0;
my $supply1 = supply whenever READY {
loop { await Promise.in(3); done if $i++> 5; emit("B"); }
}
my $supply2 = supply whenever READY {
loop { await Promise.in(1); done if $i++> 5; emit("A"); }
}
react {
whenever $supply1 -> $x { $x.print };
whenever $supply2 -> $x { $x.print };
}
Which helps because the result of a Promise will be delivered to the supply block via the thread pool scheduler, thus forcing the execution of the content of the whenever - containing the loop - into its own scheduled task.
This isn't especially pretty, but if we define a function to do it:
sub asynchronize(Supply $s) {
supply whenever Promise.kept {
whenever $s { .emit }
}
}
Then the original program only needs the addition of two calls to it:
my $i = 0;
my $supply1 = supply { loop { await Promise.in(3); done if $i++> 5; emit("B") } }
my $supply2 = supply { loop { await Promise.in(1); done if $i++> 5; emit("A") } }
react {
whenever asynchronize $supply1 -> $x { $x.print }
whenever asynchronize $supply2 -> $x { $x.print }
}
To make it work as desired. Arguably, something like this should be provided as a built-in.
It is possible to use a Channel as well, as the other solution proposes, and depending on the problem at hand that may be suitable; the question is a bit too abstracted from a real problem for me to say. This solution stays within the Supply paradigm, and is neater in that sense.
Thanks to jjmerelo here, I managed to get it working. The channel was the right track, but you actually have to consume the channels supply.
use v6;
my Channel $c .= new;
my $supply1 = start { loop { await Promise.in(1); $c.send("B"); } };
my $supply2 = start { loop { await Promise.in(0.5); $c.send("A"); } };
react
{
whenever $c.Supply -> $x { $x.print };
}
$c.close;
Additional question: How good does that scale? Can you have several thousand supplies sending to the channel?
Supplies are asynchronous, not concurrent. You will need to use channels instead of supplies to feed them concurrently.
use v6;
my $i = 0;
my Channel $c .= new;
my $supply1 = start { for ^5 { await Promise.in(1); $c.send("B"); } };
my $supply2 = start { for ^5 { await Promise.in(0.5); $c.send("A"); } };
await $supply2;
await $supply1;
$c.close;
.say for $c.list;
In this case, the two threads start at the same time, and instead of using .emit, then .send to the channel. In your example, they are effectively blocked while they wait, since they are both running in the same thread. They only give control to the other supply after the promise is kept, so that they run apparently "in parallel" and as slow as the slower of them.
Ok, so here is my real code. It seems to work, but I think there is a race condition somewhere. Here's some typical (albeit short) output.
A monster hatched.
A monster hatched.
A hero was born.
The Monster is at 2,3
The Monster is at 3,2
The Player is at 0,0
The Monster (2) attacks the Player (3)
The Monster rolls 14
The Player rolls 4
The Monster inflicts 4 damage
The Player (3) attacks the Monster (2)
The Player rolls 11
The Monster rolls 8
The Player inflicts 45 damage
The Monster is dead
The Monster is at -3,-3
The Player is at 4,-3
The Monster (1) attacks the Player (3)
The Monster rolls 8
The Player rolls 5
The Monster inflicts 11 damage
The Player has 32 hitpoints left
The Monster is at -4,1
The Player is at -1,4
The Player (3) attacks the Monster (1)
The Player rolls 12
The Monster rolls 11
The Player inflicts 46 damage
The Monster is dead
Stopping
Game over. The Player has won
Now the strange thing is, sometimes, in maybe 20% of the runs, the last line of the output is
Game over. The GameObject has won
as if the object got caught while it already is partially deconstructed? Or something? Anyway here's the code.
class GameObject
{
has Int $.id;
has Int $.x is rw;
has Int $.y is rw;
has $.game;
has Int $.speed; #the higher the faster
has Bool $.stopped is rw;
multi method start( &action )
{
start {
loop {
&action();
last if self.stopped;
await Promise.in( 1 / self.speed );
}
$.game.remove-object( self );
}
}
method speed {
$!speed +
# 33% variation from the base speed in either direction
( -($!speed / 3).Int .. ($!speed / 3).Int ).pick
;
}
}
role UnnecessaryViolence
{
has $.damage;
has $.hitpoints is rw;
has $.offense;
has $.defense;
method attack ( GameObject $target )
{
say "The {self.WHAT.perl} ({self.id}) attacks the {$target.WHAT.perl} ({$target.id})";
my $attacker = roll( $.offense, 1 .. 6 ).sum;
say "The {self.WHAT.perl} rolls $attacker";
my $defender = roll( $target.defense, 1 .. 6 ).sum;
say "The {$target.WHAT.perl} rolls $defender";
if $attacker > $defender
{
my $damage = ( 1 .. $.damage ).pick;
say "The {self.WHAT.perl} inflicts {$damage} damage";
$target.hitpoints -= $damage ;
}
if $target.hitpoints < 0
{
say "The {$target.WHAT.perl} is dead";
$target.stopped = True;
}
else
{
say "The {$target.WHAT.perl} has { $target.hitpoints } hitpoints left";
}
}
}
class Player is GameObject does UnnecessaryViolence
{
has $.name;
multi method start
{
say "A hero was born.";
self.start({
# say "The hero is moving";
# keyboard logic here, in the meantime random movement
$.game.channel.send( { object => self, x => (-1 .. 1).pick, y => (-1 .. 1).pick } );
});
}
}
class Monster is GameObject does UnnecessaryViolence
{
has $.species;
multi method start
{
say "A monster hatched.";
self.start({
# say "The monster {self.id} is moving";
# AI logic here, in the meantime random movement
$.game.channel.send( { object => self, x => (-1 .. 1).pick, y => (-1 .. 1).pick } );
});
}
}
class Game
{
my $idc = 0;
has GameObject #.objects is rw;
has Channel $.channel = .new;
method run{
self.setup;
self.mainloop;
}
method setup
{
self.add-object( Monster.new( :id(++$idc), :species("Troll"), :hitpoints(20), :damage(14), :offense(3), :speed(300), :defense(3), :x(3), :y(2), :game(self) ) );
self.add-object( Monster.new( :id(++$idc), :species("Troll"), :hitpoints(10), :damage(16), :offense(3), :speed(400), :defense(3), :x(3), :y(2), :game(self) ) );
self.add-object( Player.new( :id(++$idc), :name("Holli"), :hitpoints(50), :damage(60), :offense(3), :speed(200) :defense(2), :x(0), :y(0), :game(self) ) );
}
method add-object( GameObject $object )
{
#!objects.push( $object );
$object.start;
}
method remove-object( GameObject $object )
{
#!objects = #!objects.grep({ !($_ === $object) });
}
method mainloop
{
react {
whenever $.channel.Supply -> $event
{
self.stop-game
if self.all-objects-stopped;
self.process-movement( $event );
self.stop-objects
if self.game-is-over;
};
whenever Supply.interval(1) {
self.render;
}
}
}
method process-movement( $event )
{
#say "The {$event<object>.WHAT.perl} moves.";
given $event<object>
{
my $to-x = .x + $event<x>;
my $to-y = .y + $event<y>;
for #!objects -> $object
{
# we don't care abour ourselves
next
if $_ === $object;
# see if anything is where we want to be
if ( $to-x == $object.x && $to-y == $object.y )
{
# can't move, blocked by friendly
return
if $object.WHAT eqv .WHAT;
# we found a monster
.attack( $object );
last;
}
}
# -5 -1 5
# we won the fight or the place is empty
# so let's move
.x = $to-x == 5 ?? -4 !!
$to-x == -5 ?? 4 !!
$to-x;
.y = $to-y == 5 ?? -4 !!
$to-y == -5 ?? 4 !!
$to-y;
}
}
method render
{
for #!objects -> $object {
"The {$object.WHAT.perl} is at {$object.x},{$object.y}".say;
}
}
method stop-objects
{
say "Stopping";
for #!objects -> $object {
$object.stopped = True;
}
}
method stop-game {
"Game over. The {#!objects[0].WHAT.perl} has won".say;
$.channel.close;
done;
}
method game-is-over {
return (#!objects.map({.WHAT})).unique.elems == 1;
}
method all-objects-stopped {
(#!objects.grep({!.stopped})).elems == 0;
}
}
Game.new.run;

How to find if items in 2 list match (but can't use direct equality)

I have this code here, to see if the items in both lists are identical:
for (final car in this.cars) {
bool found = false;
for (final car2 in garage2.cars) {
if (car2.id == car.id) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
(I previously check if the 2 list lengths are equal). Is there a way to rewrite that so that I don't have O(n2) complexity?
As an option you can override quals and hashcode methods for class Car
something like this
class Car {
int id;
// other fields
#override
bool operator ==(Object o) => o is Car && id == o.id;
#override
int get hashCode => id.hashCode;
}
and then you can use the native method listEquals, which compares for deep equality.
import 'package:flutter/foundation.dart';
bool isEqual = listEquals<Car>(cars, garage2.cars);

MobX decorating a class causes function to throw TypeError

I have two data classes (business logic that I didn't write), that I'm trying to use in a store. I have fixed issues in that store by decorating them. They are defined to be:
// data classes; // these would be in separate file.
class PayRate {
/**
* #param {number} storeId The store id of the pay rate. (-1 for all)
* #param {number} empId The id of the employee. (-1 for all)
* #param {number} taskId The id of the task. (-1 for all)
* #param {Date} effectiveDate The date the payrate goes in effect.
* #param {number} rate The rate of pay.
*/
constructor(storeId, empId, taskId, effectiveDate, rate) {
this.StoreId = storeId || -1;
this.EmpId = empId || -1;
this.TaskId = taskId || -1;
this.EffectiveDate = effectiveDate ? new Date(effectiveDate) : new Date();
this.Rate = rate || 0.00;
this.OriginalObject = $.extend(true, {}, this);
}
/**
* Gets a readable version of the effective date.
* #returns {string} A -m/dd/yyyy representation of the effective date.
*/
GetReadableDate() {
return this.EffectiveDate.toLocaleDateString("en-US");
}
/**
* Gets a one line description of the pay rate.
* #returns {string} A string in the form of (date) - payrate.
*/
GetDescription() {
return `(${this.GetReadableDate()}) - $${this.Rate.toFixed(2)}`;
}
/**
* Gets a one line description of the pay rate.
* Does the exact same as GetDescription(), but is overload of Object.prototype.toString, which allows for stringification of Objects
* #returns {string} A string in the form of (date) - payrate.
*/
toString() {
return `(${this.GetReadableDate()}) - $${this.Rate.toFixed(2)}`;
}
/**
* Tells whether a pay rate was changed or not.
* #returns {boolean} A boolean saying whether or not the pay rate was changed.
*/
IsChanged() {
if (this.EffectiveDate.getTime() !== this.OriginalObject.EffectiveDate.getTime()) {
return true;
}
if (this.Rate != this.OriginalObject.Rate) {
return true;
}
if (this._deleted) {
return true;
}
return false;
}
/**
* Reverts the changes back to the original.
*/
RevertChanges() {
$.extend(true, this, this.OriginalObject);
}
}
mobx.decorate(PayRate, {
StoreId : mobx.observable,
EmpId : mobx.observable,
TaskId : mobx.observable,
EffectiveDate : mobx.observable,
Rate : mobx.observable,
})
class TaskObject {
/**
* #param {Task} task The task that is being kept track of.
* #param {PermissionLink[]} permissionLinks A list of permission links that are being kept track of.
* #param {PayRate[]} payrates A list of pay rates that are being kept track of.
*/
constructor(task, permissionLinks = [], payrates = []) {
this.Model = $.extend(true, {}, task);
this.Model.Type = 25;
this.PermissionLinks = $.extend(true, [], permissionLinks);
this.Payrates = $.extend(true, [], payrates);
this.OriginalObject = $.extend(true, {}, this);
}
/**
* Gives the dirty status of the task object.
* #returns {boolean} Tells whether or not the TaskObject has been changed.
*/
IsChanged() {
if (this.Model.Id == -1) {
return true;
}
if (this.Model.Name != this.OriginalObject.Model.Name) {
return true;
}
if (this.Model.GroupName != this.OriginalObject.Model.GroupName) {
return true;
}
if (this.Model.DescStr != this.OriginalObject.Model.DescStr) {
return true;
}
if (this.Model.IsActive != this.OriginalObject.Model.IsActive) {
return true;
}
if (this.PermissionLinks.length != this.OriginalObject.PermissionLinks.length) {
return true;
}
for (let i = 0; i < this.PermissionLinks.length; i++) {
const element = this.PermissionLinks[i];
const compElement = this.OriginalObject.PermissionLinks[i];
if (JSON.stringify(element) !== JSON.stringify(compElement)) {
return true;
}
}
for (let i = 0; i < this.Payrates.length; i++) {
const payrate = this.Payrates[i];
if (payrate.IsChanged()) {
return true;
}
}
return false
}
/**
* Reverts the changes that are on the task object.
* #returns {TaskObject} The TaskObject with its changes discarded.
*/
RevertChanges() {
this.Model = $.extend(true, {}, this.OriginalObject.Model);
this.PermissionLinks = $.extend(true, [], this.OriginalObject.PermissionLinks);
for (let i = 0; i < this.Payrates.length; i++) {
this.Payrates[i].RevertChanges()
}
return this;
}
/**
* This is here for debugging purposes (i.e. with data stores that use it) and may be overwritten with business logic at any time
*/
toString() {
return JSON.stringify(this, null, '\t')
}
}
// marking all the properties of TaskObject observable
mobx.decorate(TaskObject, {
Model : mobx.observable,
PermissionLinks : mobx.observable,
Payrates : mobx.observable,
RevertChanges : mobx.action,
IsChanged : mobx.computed
})
Now, doing new PayRate().IsChanged() works as expected, but new TaskObject.IsChanged() results in:
Uncaught TypeError: Cannot read property 'call' of undefined
at e.computeValue (mobx.umd.min.js:1)
at e.get (mobx.umd.min.js:1)
at e.read (mobx.umd.min.js:1)
at TaskObject.get (mobx.umd.min.js:1)
at pen.js:163
e.computeValue # mobx.umd.min.js:1
e.get # mobx.umd.min.js:1
e.read # mobx.umd.min.js:1
get # mobx.umd.min.js:1
(anonymous) # pen.js:163
Here is demo
Why is this happening and how can I fix it?
You declare IsChanged as a function but decorate it as a computed property, then call it as a function. If you want it to be a computed property, you'd need to:
convert it to a getter: IsChanged() -> get IsChanged()
call it without parenthesis: TaskObject.IsChanged