Calling n times the same observable - rxjs

I have a http get webservice which I need to call n times, adding the return of my last call each time (first time there is a default value) how can I do it ?

You can use 'expand' operator from rxjs. It will loop until it's supplied with empty() observable. Here is example:
import { empty } from 'rxjs';
private service; <--- service that gives us the observable by some value
private initialValue: number = 5;
private counter: number = 0;
private source$: Observable<number> = this.service.getSourceWithValue(initialValue);
source$.pipe(
expand(value => isCounterExceeded()
? incrementCounterAndGetNextSourceObservableWithValue(value);
: empty()
);
// if counter is not exceeded we will increment the counter and create another
// observable based on current value. If it is exceeded, we are stopping the loop by
// returning the empty() observable
private incrementCounterAndGetNextSourceObservableWithValue(value: number): Observable<number> {
this.counter++;
return this.service.getSourceWithValue(value);
}
private isCounterExceeded() {
return this.counter >= 4;
}

This sounds like you could use expand:
const N = 4;
const source = of(1).pipe(
expand((previous, index) => index === 4 ? EMPTY : of(previous * 2))
);
source.subscribe(console.log);
Live demo: https://stackblitz.com/edit/rxjs-fcpin2

Related

RxJS: How to create an event manager with a buffer that flush based on multiple conditions

I have a requirement to create an event manager with a buffer that flushes if one of 3 criteria is met:
2 seconds pass
50 events received
Flush on demand if requested by user
All criteria will reset when buffer flushes (reset the 2 second timer, reset the 50 events count...etc)
This is what I've implemeted so far and it seems to be working but I'm wondering if there's a better way to achieve this requirement.
import { interval, merge, Subject, Subscription } from "rxjs";
import { bufferWhen, filter, tap } from "rxjs/operators";
class Foo {
private eventListener: Subject < string > = new Subject();
private eventForceFlushListener: Subject < void > = new Subject();
private eventBufferSizeListener: Subject < void > = new Subject();
private maxBufferSize = 50;
private currentBufferSize = 0;
/**
*
* Buffer that will flush if one of the 3 criteria is met:
* - 50 texts are received
* - 2 seconds pass
* - Force flush by user
*/
private eventBufferOperator = () => merge(interval(2 * 1000), this.eventForceFlushListener, this.eventBufferSizeListener);
/**
* Flush buffer if requested by user. (for example flush buffer before app close so we dont lose buffered texts)
*/
public forceFlush() {
this.eventForceFlushListener.next();
}
/**
* Method used by users to emit texts to the listener
*/
public emitText(text: string) {
this.eventListener.next(text);
this.currentBufferSize = this.currentBufferSize + 1;
if (this.currentBufferSize == this.maxBufferSize) {
// flush all evenst when maxBufferSize is reached
this.eventBufferSizeListener.next();
// buffer size is reset below in the function that's inside "subscribe"
}
}
public subscribeToEventListerenr() {
const eventListenerSubscription = this.eventListener
.pipe(
tap((text) => text.trim()),
filter((text) => true),
bufferWhen(this.eventBufferOperator),
filter((events) => !!events.length)
)
.subscribe((x) => {
console.log(x);
this.maxBufferSize = 0; // reset size buffer
});
return eventListenerSubscription;
}
}
Users then can use this event manager as follows:
const eventManager = new Foo();
eventManager.subscribeToEventListerenr();
eventManager.emitText('message1');
eventManager.emitText('message2');
5 seconds pass
5 events received
Flush on demand if requested by user
const { race, Subject, take, share, buffer, tap, bufferCount, bufferTime, startWith, exhaustMap, map } = rxjs;
const observer = (str) => ({
subscribe: () => console.log(`${str} -> subscribe`),
next: () => console.log(`${str} -> next`),
unsubscribe: () => console.log(`${str} -> unsubscribe`),
});
const event$ = new Subject()
const share$ = event$.pipe(map((_, i) => i + 1), share());
const flush$ = new Subject();
const trigger$ = flush$.pipe(tap(observer('flush$')));
const bufferSize$ = share$.pipe(startWith(null), bufferCount(5), tap(observer('BufferSize 5')));
const bufferTime$ = share$.pipe(bufferTime(5000), tap(observer('5 Sec')));
const race$ = race(bufferTime$, trigger$, bufferSize$).pipe(take(1));
const buffer$ = share$.pipe(exhaustMap(() => race$));
share$.pipe(buffer(buffer$)).subscribe((x) => console.log(x));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.5.6/rxjs.umd.min.js"></script>
<button onclick="event$.next()">event</button>
<button onclick="flush$.next()">flush</button>
https://stackblitz.com/edit/rxjs-5qgaeq
My final answer
window
bufferTime
const bufferBy$ = new Subject<void>();
const maxBufferSize = 3;
const bufferTimeSpan = 5000;
source$.pipe(
window(bufferBy$),
mergeMap(bufferTime(bufferTimeSpan, null, maxBufferSize)),
).subscribe(...);
https://stackblitz.com/edit/rxjs-hoafkr

In Rx instead of only getting the last debounced object, can I get the complete sequence?

I want to know if one of the debounced objects was a green ball. Filtering for only green balls before or after the debounce leads to incorrect behavior.
You can use the buffer operator together with the debounce operator. Here a very basic example:
// This is our event stream. In this example we only track mouseup events on the document
const move$ = Observable.fromEvent(document, 'mouseup');
// We want to create a debounced version of the initial stream
const debounce$ = move$.debounceTime(1000);
// Now create the buffered stream from the initial move$ stream.
// The debounce$ stream can be used to emit the values that are in the buffer
const buffered$ = move$.buffer(debounce$);
// Subscribe to your buffered stream
buffered$.subscribe(res => console.log('Buffered Result: ', res));
If I understand correctly what you want to achieve, you probably need to build an Observable which emits some sort of object which contains both the source value (i.e. blue, red, green in your case) as well as a flag that indicates whether or not there was a green in the debounced values.
If this is true, you can try to code along these lines
const s = new Subject<string>();
setTimeout(() => s.next('B'), 100);
setTimeout(() => s.next('G'), 1100);
setTimeout(() => s.next('B'), 1200);
setTimeout(() => s.next('G'), 1300);
setTimeout(() => s.next('R'), 1400);
setTimeout(() => s.next('B'), 2400);
let hasGreen = false;
s
.do(data => hasGreen = hasGreen || data === 'G')
.debounceTime(500)
.map(data => ({data, hasGreen})) // this map has to come before the following do
.do(() => hasGreen = false)
.subscribe(data => console.log(data))
Be careful about the sequence. In particular you have to put the map operator which creates the object you want to emit before the do that resets your variable.
This could be done with a non-trivial set of operators and side-effecting a flow by introducing extra channels:
import java.util.Queue;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Test;
import io.reactivex.*;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.*;
import io.reactivex.subjects.PublishSubject;
public class DebounceTimeDrop {
#Test
public void test() {
PublishSubject<Integer> source = PublishSubject.create();
TestScheduler scheduler = new TestScheduler();
source.compose(debounceTime(10, TimeUnit.MILLISECONDS, scheduler, v -> {
System.out.println(
"Dropped: " + v + " # T=" + scheduler.now(TimeUnit.MILLISECONDS));
}))
.subscribe(v -> System.out.println(
"Passed: " + v + " # T=" + scheduler.now(TimeUnit.MILLISECONDS)),
Throwable::printStackTrace,
() -> System.out.println(
"Done " + " # T=" + scheduler.now(TimeUnit.MILLISECONDS)));
source.onNext(1);
scheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS);
scheduler.advanceTimeBy(20, TimeUnit.MILLISECONDS);
source.onNext(2);
scheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS);
source.onNext(3);
scheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS);
source.onNext(4);
scheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS);
source.onNext(5);
scheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS);
scheduler.advanceTimeBy(20, TimeUnit.MILLISECONDS);
source.onNext(6);
scheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS);
scheduler.advanceTimeBy(20, TimeUnit.MILLISECONDS);
source.onComplete();
}
public static <T> ObservableTransformer<T, T> debounceTime(
long time, TimeUnit unit, Scheduler scheduler,
Consumer<? super T> dropped) {
return o -> Observable.<T>defer(() -> {
AtomicLong index = new AtomicLong();
Queue<Timed<T>> queue = new ConcurrentLinkedQueue<>();
return o.map(v -> {
Timed<T> t = new Timed<>(v,
index.getAndIncrement(), TimeUnit.NANOSECONDS);
queue.offer(t);
return t;
})
.debounce(time, unit, scheduler)
.map(v -> {
while (!queue.isEmpty()) {
Timed<T> t = queue.peek();
if (t.time() < v.time()) {
queue.poll();
dropped.accept(t.value());
} else
if (t == v) {
queue.poll();
break;
}
}
return v.value();
})
.doOnComplete(() -> {
while (!queue.isEmpty()) {
dropped.accept(queue.poll().value());
}
});
});
}
}
prints
Passed: 1 # T=10
Dropped: 2 # T=43
Dropped: 3 # T=43
Dropped: 4 # T=43
Passed: 5 # T=43
Passed: 6 # T=73
Done # T=93

RepeatWhen in combination Observable.of(x) has unexpected behavior

I am having unexpected behavior with Observable.of() and repeatWhen. I was wondering if this is correct behavior or not, and why?
const value = 5;
let variable = 0;
const getValue = () => {
variable = variable + 1;
return value * variable;
}
function test () {
Observable.of(getValue())
.repeatWhen(obs => obs.delay(1000))
.subscribe(value => console.log(value);
}
Expected: 5 10 15 20 ...
Result: 5 5 5 5 ...
Apparently, the value returned by Observable.of() is reused for each subsequent subscribe. How, why?
The problem is that getValue() is evaluated only once and immediately. That's got nothing to do with rxjs, it's just how Javascript works. You need to evaluate it on each retry instead, which you can do by using defer:
Observable.defer(() => Observable.of(getValue()))
.repeatWhen(obs => obs.delay(1000))
.subscribe(console.log);
The problem is with use of value. You are changing variable not value(also the value is available in two scopes i.e. Global and closer scope).
To solve the issue, Change the definition of getValue as following:
const getValue = () => {
variable = variable + 1;
value = value * variable;
return value;
}
So, corrected code will look like:
const value = 5;
let variable = 0;
const getValue = () => {
variable = variable + 1;
value = value * variable;
return value;
}
function test () {
Observable.of(getValue())
.repeatWhen(obs => obs.delay(1000))
.subscribe(value => console.log(value);
}

How can I get the time it takes a function to test the performance of functions in Kotlin

I need to check how long does a function need to run. I have the following functions which address the same task:
mixAnimalsA
fun mixAnimalsA(a1: Animal, a2: Animal) =
when (setOf(a1, a2)) {
setOf(Animal.OWL, Animal.Leopard) -> Beast.OWLPARD
setOf(Animal.ELEPHANT, Animal.BUTTERFLY) -> Beast.BUTTERPHANT
else -> throw Exception("Not possible combination")
}
mixAnimalsB
fun mixAnimalsB(a1: Animal, a2: Animal) =
when (setOf(a1, a2)) {
(c1 == Animal.OWL && c2 == Animal.Leopard) ||
(c2 == Animal.OWL && c1 == Animal.Leopard) -> Beast.OWLPARD
(c1 == Animal.ELEPHANT && c2 == Animal.BUTTERFLY) ||
(c2 == Animal.ELEPHANT && c1 == Animal.BUTTERFLY)-> Beast.BUTTERPHANT
else -> throw Exception("Not possible combination")
}
Animal and Beast are enumerations. How can I measure how long each function takes to run?
If you're looking for an in-code solution, you can use measureTimeMillis and measureNanoTime, like this:
val time = measureTimeMillis {
// call your function here
}
They return the measured time in milliseconds and nanoseconds, respectively.
Measure execution time and also keep the result
Standard Library
The standard library function measureTimedValue may be used to measure execution time and at the same time capture the result. This tuple of values is being exposed as a TimedValue(value: T, duration: Duration):
#ExperimentalTime
fun main() {
val (result: String, duration: Duration) = measureTimedValue {
operation()
}
print("Got $result after ${duration.inMilliseconds} ms.")
}
Note that this API is experimental and requires explicit opt-in.
Obsolete custom implementation
(This used to be my answer before the standard lib was extended)
If you want to measure the execution time and also access the measured function's return value afterward, here's a custom solution:
inline fun <R> executeAndMeasureTimeMillis(block: () -> R): Pair<R, Long> {
val start = System.currentTimeMillis()
val result = block()
return result to (System.currentTimeMillis() - start)
}
You can call it like this:
val (response, duration) = executeAndMeasureTimeMillis {
restTemplate.getForObject<AnyObject>(uri)
}
If it's enough to get the time as output on the console:
fun <T> timeIt(message: String = "", block: () -> T): T {
val start = System.currentTimeMillis()
val r = block()
val end = System.currentTimeMillis()
println("$message: ${end - start} ms")
return r
}
Usage:
val result = timeIt("note about the code") {
// do something...
}
Output (example):
note about the code: 1ms
For the benchmark of some code block and getting the result a same time, i do some refactor of the standard method in TimingKt class
to give us output generic result and at the same time display a given log.
Here is my example :
/**
* Executes the given block and show the elapsed time in milliseconds in a given message.
*
* #param block which will be bench marked
* #param logMessage log message to be displayed
*
* #return a generic result
*
*/
private fun <T> measureTime(block: () -> T, logMessage: String): T {
val start = System.currentTimeMillis()
val t = block()
val consumedTime = System.currentTimeMillis() - start
Log.d(TAG, "Calculation of $logMessage time :$consumedTime milliseconds")
return t
}
And how it will be used :
return measureTime({
// given block with return result
}, "message to be displayed typically the name of method which will be calculated")
This is my simple time test code.
class TimeCounter(val name: String) {
var totalTime: Long = 0
private set
var count: Int = 0
private set
var minTime: Long = Long.MAX_VALUE
private set
var maxTime: Long = Long.MIN_VALUE
private set
fun addTime(time: Long) {
this.count++
this.totalTime += time
if (this.minTime > time) {
this.minTime = time
}
if (this.maxTime < time) {
this.maxTime = time
}
}
val averageTime: Double
get() = this.totalTime / this.count.toDouble()
fun printTime() {
println("(time about : '$name'), totalTime : $totalTime, count : $count, " +
"average : $averageTime, minTime : $minTime, maxTime : $maxTime")
}
fun <T> runWithTimeCount(run: () -> T): T {
val startTime = System.currentTimeMillis()
return run().also {
this.addTime(System.currentTimeMillis() - startTime)
}
}
}
you can use 'TimeCounter' like this, (example)
var sum = 0
val testTimeCounter = TimeCounter("logic1")
for(i in 0 until 100){
sum += testTimeCounter.runWithTimeCount {
logic1(i) // your logic
}
}
println(sum)
testTimeCounter.printTime() // totalTime, average, minTime, maxTime
Execute function, measure its performance and logs performance - in same call
this solution will help folks who want to measure and log performance, execute function at same time, also is a cleaner approach when there is multiple performance measurement involved of different functions
Create functions as such:
//the inline performance measurement method
private inline fun <T> measurePerformanceInMS(
logger: (Long) -> Unit,
function: () -> T)
: T {
val startTime = System.currentTimeMillis()
val result: T = function.invoke()
val endTime = System.currentTimeMillis()
logger.invoke( endTime - startTime)
return result
}
//the logger function
fun logPerf(time: Long){
Log.d("TAG","PERFORMANCE IN MS: $time ms ")
}
//the function whose performance needs to be checked
fun longRunningFunction() : Int{
var x = 0
for (i in 1..20000) x++
return x
}
This way you can keep logging, performance computation and function execution under a single function call and no code replication needed.
If you require nano second measurement then use System.nanoTime()
USAGE :
val endResult = measurePerformanceInMS({time -> logPerf(time)}){
longRunningFunction()
}
NOTE : here the 'endResult' will carry the result of function whose performance was being measured.

How do you use linq to group records based on an accumulator?

Given an enumeration of records in the format:
Name (string)
Amount (number)
For example:
Laverne 4
Lenny 2
Shirley 3
Squiggy 5
I want to group the records, so that each group's total Amount does not exceed some limit-per-group. For example, 10.
Group 1 (Laverne,Lenny,Shirley) with Total Amount 9
Group 2 (Squiggy) with Total Amount 5
The Amount number is guaranteed to always be less than the grouping limit.
If you allow for captured variables to maintain state, then it becomes easier. If we have:
int limit = 10;
Then:
int groupTotal = 0;
int groupNum = 0;
var grouped = records.Select(r =>
{
int newCount = groupTotal + r.Amount;
if (newCount > limit)
{
groupNum++;
groupTotal = r.Amount;
}
else
groupTotal = newCount;
return new{Records = r, Group = groupNum};
}
).GroupBy(g => g.Group, g => g.Records);
It's O(n), and just a Select and a GroupBy, but the use of captured variables may not be as portable across providers as one may want though.
For linq-to-objects though, it's fine.
Here I have a solution using only LINQ functions:
// Record definition
class Record
{
public string Name;
public int Amount;
public Record(string name, int amount)
{
Name = name;
Amount = amount;
}
}
// actual code for setup and LINQ
List<Record> records = new List<Record>()
{
new Record("Laverne", 4),
new Record("Lenny", 2),
new Record("Shirley", 3),
new Record("Squiggy", 5)
};
int groupLimit = 10;
// the solution
List<Record[]> test =
records.GroupBy(record => records.TakeWhile(r => r != record)
.Concat(new[] { record })
.Sum(r => r.Amount) / (groupLimit + 1))
.Select(g => g.ToArray()).ToList();
This gives the correct result:
test =
{
{ [ "Laverne", 4 ], [ "Lenny", 2 ], [ "shirley", 3 ] },
{ [ "Squiggly", 5 ] }
}
The only downside is that this is O(n2). It essentially groups by the index of the group (as defined by using the sum of the record up to the current one). Note that groupLimit + 1 is needed so that we actually include groups from 0 to groupLimit, inclusive.
I'm trying to find a way of making it prettier, but it doesn't look easy.
A dotnet fiddle with a solution using Aggregate:
https://dotnetfiddle.net/gVgONH
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
// Record definition
public class Record
{
public string Name;
public int Amount;
public Record(string name, int amount)
{
Name = name;
Amount = amount;
}
}
public static void Main()
{
// actual code for setup and LINQ
List<Record> records = new List<Record>()
{
new Record("Alice", 1), new Record("Bob", 5), new Record("Charly", 4), new Record("Laverne", 4), new Record("Lenny", 2), new Record("Shirley", 3), new Record("Squiggy", 5)}
;
int groupLimit = 10;
int sum = 0;
var result = records.Aggregate(new List<List<Record>>(), (accumulated, next) =>
{
if ((sum + next.Amount >= groupLimit) || accumulated.Count() == 0)
{
Console.WriteLine("New team: " + accumulated.Count());
accumulated.Add(new List<Record>());
sum = 0;
}
sum += next.Amount;
Console.WriteLine("New member {0} ({1}): adds up to {2} ", next.Name, next.Amount, sum);
accumulated.Last().Add(next);
return accumulated;
}
);
Console.WriteLine("Team count: " + result.Count());
}
}
With output:
New team: 0
New member Alice (1): adds up to 1
New member Bob (5): adds up to 6
New team: 1
New member Charly (4): adds up to 4
New member Laverne (4): adds up to 8
New team: 2
New member Lenny (2): adds up to 2
New member Shirley (3): adds up to 5
New team: 3
New member Squiggy (5): adds up to 5
Team count: 4
There is no 'performant' way to do this with the built in Linq operators that I am aware of. You could create your own extension method, though:
public static class EnumerableExtensions
{
public static IEnumerable<TResult> GroupWhile<TSource, TAccumulation, TResult>(
this IEnumerable<TSource> source,
Func<TAccumulation> seedFactory,
Func<TAccumulation, TSource, TAccumulation> accumulator,
Func<TAccumulation, bool> predicate,
Func<TAccumulation, IEnumerable<TSource>, TResult> selector)
{
TAccumulation accumulation = seedFactory();
List<TSource> result = new List<TSource>();
using(IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while(enumerator.MoveNext())
{
if(!predicate(accumulator(accumulation, enumerator.Current)))
{
yield return selector(accumulation, result);
accumulation = seedFactory();
result = new List<TSource>();
}
result.Add(enumerator.Current);
accumulation = accumulator(accumulation, enumerator.Current);
}
if(result.Count > 0)
{
yield return selector(accumulation, result);
}
}
}
}
And then call it like this:
int limit = 10;
var groups =
records
.GroupWhile(
() => 0,
(a, x) => a + x,
(a) => a <= limit,
(a, g) => new { Total = a, Group = g });
The way it is currently written, if any single record exceeds that limit then that record is returned by itself. You could modify it to exclude records that exceed the limit or leave it as is and perform the exclusion with Where.
This solution has O(n) runtime.

Resources