Get last value from incomplete observable - rxjs

There is an incomplete observable which can have or not have a replay of n values. I would like to get the last value from it - or just the next one if there is none yet.
This works for first available value with first() and take(1) (example):
possiblyReplayedIncomplteObservable.first().toPromise().then(val => ...);
But for the last value both last() and takeLast(1) wait for observable completion - not the desirable behaviour here.
How can this be solved? Is there a specific operator for that?

I had a solution for ReplaySubject(2) that 'drains' the sequence to get the latest element and if the sequence is empty simply takes the last element, yet, it was cumbersome and did not scale well (for example, if you decide to increase the replay size to 3). I then remembered that Replay/Behavior subjects tend to be hard to manage when they are piped. The simplest solution to that is to create a 'shadow' sequence and pipe your ReplaySubject into it (instead of creating it by transformation/operation on your ReplaySubject), hence:
var subject$ = new Rx.ReplaySubject(3);
var lastValue$ = new Rx.ReplaySubject(1);
subject$.subscribe(lastValue$); // short hand for subject$.subscribe(v => lastValue$.next(v))
lastValue$.take(1).toPromise().then(...);
========== Old solutions, ignoring the ReplaySubject(2) =================
After reading the comment below, the correct code is:
Rx.Observable.combineLatest(possiblyReplayedIncomplteObservable).take(1).subscribe(...)
and not
Rx.Observable.combineLatest(possiblyReplayedIncomplteObservable).subscribe(...)
This is due to the fact the promise is a "one time" observable. I think the toPromise() code resolves the result only on completion.
The take(1) will not affect your original stream since it operates on the new stream which is created by combineLatest.
And actually, the simplest way is:
possiblyReplayedIncomplteObservable.take(1).toPromise().then(...)

Related

RxJS Observables: new request depending on previous result

How can I generate new values to an Observable or Subject depending on received values?
Example:
Let's say I have an Observable that emits one random number thanks to a web API.
If it is an even number, I want the Observable to emit another random number using the first one as a seed... and so on until I get an odd value.
Note that I don't know in advance how many requests I'm going to make.
Until now, I managed to do it with "weird", recursive methods, but I feel like there must be a much proper way to do this.
Seems like you can use expand() for this.
const source$ = /* some API call that returns an Observable */;
source$.pipe(
expand((previous: number) => previous % 2 === 0 ? source$ : EMPTY),
takeLast(1),
).subscribe(console.log);
This gives you only the last value (the first odd). If you want to get all the intermediate values as well just remove that takeLast(1).
Live demo: https://stackblitz.com/edit/rxjs-czomtm

Logging and asserting the number of previously-unknown DOM elements

I'ts my first tme using Cypress and I almost finalized my first test. But to do so I need to assert against a unknown number. Let me explain:
When the test starts, a random number of elements is generated and I shouldn't have control on such a number (is a requirement). So, I'm trying to get such number in this way:
var previousElems = cy.get('.list-group-item').its('length');
I'm not really sure if I'm getting the right data, since I can not log it (the "cypress console" shows me "[Object]" when I print it). But let's say such line returns (5) to exemplify.
During the test, I simulate a user creating extra elements (2) and removing an (1) element. Let's say the user just creates one single extra element.
So, at the end os the test, I need to check if the number of eements with the same class are equals to (5+2-1) = (6) elements. I'm doing it in this way:
cy.get('.list-group-item').its('length').should('eq', (previousTasks + 1));
But I get the following message:
CypressError: Timed out retrying: expected 10 to equal '[object Object]1'
So, how can I log and assert this?
Thanks in advance,
PD: I also tryed:
var previousTasks = (Cypress.$("ul").children)? Cypress.$("ul").children.length : 0;
But it always returns a fixed number (2), even if I put a wait before to make sure all the items are fully loaded.
I also tryed the same with childNodes but it always return 0.
Your problem stems from the fact that Cypress test code is run all at once before the test starts. Commands are queued to be run later, and so storing variables as in your example will not work. This is why you keep getting objects instead of numbers; the object you're getting is called a chainer, and is used to allow you to chain commands off other commands, like so: cy.get('#someSelector').should('...');
Cypress has a way to get around this though; if you need to operate on some data directly, you can provide a lambda function using .then() that will be run in order with the rest of your commands. Here's a basic example that should work in your scenario:
cy.get('.list-group-item').its('length').then(previousCount => {
// Add two elements and remove one...
cy.get('.list-group-item').its('.length').should('eq', previousCount + 1);
});
If you haven't already, I strongly suggest reading the fantastic introduction to Cypress in the docs. This page on variables and aliases should also be useful in this case.

RxJS keep old and new value on emission

I have been trying to get the old and new value in every emission. I have seen the option of using pairwise or bufferCount but they don't allow to keep the first value.
The goal would to go from:
---1---2---3---4---5---
To:
---null,1---1,2---2,3---3,4---4,5---
Any ideas?
You can use startWith(null) to initialize the operator (whichever you use) and then it'll emit on every value:
// or bufferCount(2, 1)
source.startWith(null).pairwise().subscribe(...)

Accomplishing shareValue/shareBehavior

I have an Observable that is based on some events and at some point does some expensive computation. I would like to render the results from that Observable in multiple different places. If I naively subscribe to this Observable in two places I will end up doing the expensive computation twice. Here is a code snippet to drive my point home:
var s = new rx.Subject();
var o = s.map(x => { console.log('expensive computation'); return x });
o.subscribe(x => console.log('1: ' + x));
o.subscribe(x => console.log('2: ' + x));
s.next(42);
The output is:
expensive computation
1: 42
expensive computation
2: 42
I would like to perform the expensive computation in the map only once. share accomplishes this, but it makes it so late-arriving subscribers do not get the current value to render. In previous RxJS versions, shareValue allowed late-arriving subscribers to get the current value. However, it appears that this was renamed to shareBehavior in RxJS 5 and then removed altogether:
https://github.com/ReactiveX/rxjs/pull/588
https://github.com/ReactiveX/rxjs/pull/712
There is a long discussion in this issue where it was decided that they would 'Remove shareBehavior and shareReplay to prevent user confusion.' I don't understand what the potential for confusion was (so maybe that means I am one of the users saved by this decision?).
publishBehavior also looks promising but I don't fully understand publish and it seems like it adds more complexity than I need or want.
Anyway, I would like to know if there is a recommended way to accomplish this in RxJS 5. The migration doc doesn't provide any recommendations.
After some more research I've found that the behavior I've described can be implemented with
.publishBehavior(startValue).refCount().
This discovery is based on the fact that share() is an alias for publish().refCount(). I still don't fully understand publish() but this seems to have the desired effect in practice.
Similar is cache(1) (which is an alias for publishReplay(1).refCount()). It has a similar effect as publishBehavior(defaultValue).refCount() except that it does not start with a default value. So if no items have been emitted, new subscribers will not immediately receive a value.

confirm conditional statement applies to >0 observations in Stata

This is something that has puzzled me for some time and I have yet to find an answer.
I am in a situation where I am applying a standardized data cleaning process to (supposedly) similarly structured files, one file for each year. I have a statement such as the following:
replace field="Plant" if field=="Plant & Machinery"
Which was a result of the original code-writing based on the data file for year 1. Then I generalize the code to loop through the years of data. The problem becomes if in year 3, the analogous value in that variable was coded as "Plant and MachInery ", such that the code line above would not make the intended change due to the difference in the text string, but not result in an error alerting the change was not made.
What I am after is some sort of confirmation that >0 observations actually satisfied the condition each instance the code is executed in the loop, otherwise return an error. Any combination of trimming, removing spaces, and standardizing the text case are not workaround options. At the same time, I don't want to add a count if and then assert statement before every conditional replace as that becomes quite bulky.
Aside from going to the raw files to ensure the variable values are standardized, is there any way to do this validation "on the fly" as I have tried to describe? Maybe just write a custom program that combines a count if, assert and replace?
The idea has surfaced occasionally that replace should return the number of observations changed, but there are good reasons why not, notably that it is not a r-class or e-class command any way and it's quite important not to change the way it works because that could break innumerable programs and do-files.
So, I think the essence of any answer is that you have to set up your own monitoring process counting how many values have (or would be) changed.
One pattern is -- when working on a current variable:
gen was = .
foreach ... {
...
replace was = current
replace current = ...
qui count if was != current
<use the result>
}

Resources