0

I have two hot observables which I do not want to miss any notification

1st observable produces numbers

1-2-3-4

and 2nd strings

a-b

I am looking for a way to zip them to produce the next output

a-1 b-2 a-3 b-4 a-5

So what I want is to repeat the 2nd stream indefinitely until the first stream is unsubscribed. I tried something like the next test which produces the desired output. However I do not Repeat for ever as I do not know how to stop in order to test the results. In addition I used a ReplaySubject which I completed manually meaning that I cannot receive any new notifications.

    class MyClass:ReactiveTest{
        [Fact]
        public void MethodName4() {
            var strings = new ReplaySubject<string>();
            var testScheduler = new TestScheduler();
            testScheduler.Schedule("", TimeSpan.FromTicks(10), (scheduler, s) => {
                strings.OnNext("a");
                strings.OnNext("b");
                strings.OnCompleted();
            });

            var numbers = testScheduler.CreateHotObservable(OnNext(100, 1), OnNext(200, 2), OnNext(300, 3), OnNext(400, 4), OnNext(500, 5));
            numbers.Zip(Observable.Defer(() => strings).Repeat(3), (i, s) => (i, s)).Subscribe();
            testScheduler.AdvanceBy(500);
        }

    }
Apostolis Bekiaris
  • 2,145
  • 2
  • 18
  • 21

2 Answers2

1

I think I found a solution which I will try to describe.

It makes no sense to repeat a hot observable as it never completes. However you can repeat a window of this hot observable. I refactored this answer from @Enigmativity to repeat all values instead of the last one.

    public static IObservable<T> RepeatAllDuringSilence<T>(this IObservable<T> source, TimeSpan maxQuietPeriod,
        IScheduler scheduler=null, IObservable<T> inner=null) {
        if (scheduler==null)
            scheduler=Scheduler.Default;
        if (inner == null)
            inner = source;
        return Observable.Create<T>(observer => {
            var replay = inner.Replay(scheduler);
            var sequence = source.Select(x => Observable.Interval(maxQuietPeriod, scheduler).SelectMany(l => replay).StartWith(x)).Switch();
            return new CompositeDisposable(replay.Connect(), sequence.Subscribe(observer));
        });
    }

then using this method in my test I manage to get the expected results without a completing ReplaySubjector losing future notification because i change the temperature of my original observable.

class MyClass : ReactiveTest {
    [Fact]
    public void MethodName4() {
        var strings = new Subject<string>();
        var testScheduler = new TestScheduler();
        testScheduler.Schedule("", TimeSpan.FromTicks(10), (scheduler, s) => {
            strings.OnNext("a");
            strings.OnNext("b");
        });
        testScheduler.Schedule("", TimeSpan.FromTicks(20), (scheduler, s) => {
            strings.OnNext("c");
        });

        var numbers = testScheduler.CreateHotObservable(OnNext(10, 1), OnNext(20, 2), OnNext(30, 3), OnNext(40, 4), OnNext(50, 5));
        numbers.Zip(strings.RepeatAllDuringSilence(TimeSpan.FromTicks(10),testScheduler), (i, s) => (i, s)).Subscribe(tuple => WriteLine(tuple));
        testScheduler.AdvanceBy(50);
    }

}

(1, a)
(2, b)
(3, c)
(4, a)
(5, b)

Apostolis Bekiaris
  • 2,145
  • 2
  • 18
  • 21
0

Doesn't this solve your problem? :

  void TestMergedPairs()
    {
        var source1 = Observable.Range(1,9999);
        var source2 = Observable.Range(65, 2).Repeat();


        var z = source1.Zip(source2,(x,y)=> { return new { left = x, right = y }; })
             .TakeWhile((v)=>v.left<1000);


        z.Subscribe(x => { Console.WriteLine( x.left +" "+x.right ); });
    }

If not, then it is not clear what your problem is. If you want to be able to keep repeating the historical contents of the second observable, in the sense that your "a","b" might later become "a","b","c" and then that 3 letter sequence should be iterated, then perhaps your source could be the result of a .Scan that aggregates historical windows.

Sentinel
  • 3,582
  • 1
  • 30
  • 44
  • Thanks for the answer, your assumptions are correct for what I want. However as I wrote in my original post, when using a `Subject` and not a `cold` Observable as the `Range` is, the `Repeat` operator does nothing. I appreciate If you could provide a solution using the two streams of my original post an by looking at the answer I posted which describes the case better. – Apostolis Bekiaris Jan 29 '18 at 13:11
  • So, to clarify, you want your string observable to be hot, and you want the 'history so far' to keep repeating (to be zipped with the number stream)? So if for example your string observable has a history of "a","b", it should zip this with the numbers until the next "c" arrives, and from then on it should zip with {"a","b","c"}.Repeat? Do I understand your problem? – Sentinel Jan 29 '18 at 17:10
  • exactly in addition the numbers is hot also – Apostolis Bekiaris Jan 29 '18 at 20:31