C# 合并两个观测值,但仅当第一个观测值紧跟第二个观测值时

C# 合并两个观测值,但仅当第一个观测值紧跟第二个观测值时,c#,system.reactive,C#,System.reactive,我的目标可能是用大理石图最容易解释的。 我有两个观测值,xs和ys。我想返回一个名为rs的可观测值 xs --x---x---x---x-------x---x---x- \ \ \ ys ----y-----------y---y---y------- | | | rs ----x-----------x-------x------- y y y 所以,我需

我的目标可能是用大理石图最容易解释的。 我有两个观测值,xs和ys。我想返回一个名为rs的可观测值

xs --x---x---x---x-------x---x---x-
      \           \       \
ys ----y-----------y---y---y-------
       |           |       |
rs ----x-----------x-------x-------
       y           y       y
所以,我需要类似于CombineTest的东西,除了它应该只在xs后面跟ys时启动。该模式之外的其他X或Y不应触发输出,应丢弃


CombineTest、Zip或And/Then/When不执行我需要的操作,并且我找不到任何方法来指定更复杂的联接结构。

要以另一种方式陈述关系图,每个x需要取第一个y,而在y之前不需要额外的x。这些第一个要求建议
采取(1)
。此时,您将有一个
IObservable
。第二项要求和前两项生成的类型指向
开关
。总而言之,您应该能够将图表与以下内容相匹配:

var rs = xs.Select(x => ys.Take(1)
                          .Select(y => Tuple.Create(x, y))
                  )
           .Switch()
我最终使用了Join

var rs = xs.Join(ys, 
                 _ => xs.Merge(ys),
                 _ => Observable.Empty<Unit>(),
                 Tuple.Create);
var rs=xs.Join(ys,
_=>xs.Merge(ys),
_=>Observable.Empty(),
Tuple.Create);
这篇文章很好地解释了Join的工作原理:

对于此类问题,提供测试场景非常有用。您已经给出了一个很好的小大理石图,可以很容易地将其转换为测试用例。然后,其他论坛读者可以直接获取测试代码,实现他们的想法,并知道他们是否满足您的需求

[Test]
public void Should_only_get_latest_value_from_Y_but_not_while_x_produes()
{
    //            11111111112222222222333
    //   12345678901234567890123456789012
    //xs --x---x---x---x-------x---x---x-
    //      \           \       \
    //ys ----y-----------y---y---y-------
    //       |           |       |
    //rs ----x-----------x-------x-------
    //       y           y       y


    var testScheduler = new TestScheduler();
    //            11111111112222222222333
    //   12345678901234567890123456789012
    //xs --x---x---x---x-------x---x---x-
    var xs = testScheduler.CreateColdObservable(
        new Recorded<Notification<char>>(3, Notification.CreateOnNext('1')),
        new Recorded<Notification<char>>(7, Notification.CreateOnNext('2')),
        new Recorded<Notification<char>>(10, Notification.CreateOnNext('3')),
        new Recorded<Notification<char>>(15, Notification.CreateOnNext('4')),
        new Recorded<Notification<char>>(23, Notification.CreateOnNext('5')),
        new Recorded<Notification<char>>(27, Notification.CreateOnNext('6')),
        new Recorded<Notification<char>>(31, Notification.CreateOnNext('7')));

    //            11111111112222222222333
    //   12345678901234567890123456789012
    //ys ----y-----------y---y---y-------
    var ys = testScheduler.CreateColdObservable(
        new Recorded<Notification<char>>(5, Notification.CreateOnNext('A')),
        new Recorded<Notification<char>>(17, Notification.CreateOnNext('B')),
        new Recorded<Notification<char>>(21, Notification.CreateOnNext('C')),
        new Recorded<Notification<char>>(25, Notification.CreateOnNext('D')));


    //Expected :
    //Tick  x   y
    //5     1   A
    //17    4   B
    //25    5   D
    var expected = new[]
    {
        new Recorded<Notification<Tuple<char, char>>>(5, Notification.CreateOnNext(Tuple.Create('1', 'A'))),
        new Recorded<Notification<Tuple<char, char>>>(17, Notification.CreateOnNext(Tuple.Create('4', 'B'))),
        new Recorded<Notification<Tuple<char, char>>>(25, Notification.CreateOnNext(Tuple.Create('5', 'D')))
    };

    var observer = testScheduler.CreateObserver<Tuple<char, char>>();

    //Passes HOT, fails Cold. Doesn't meet the requirements due to Timeout anyway.
    //xs.Select(x => ys.Take(1)
    //                    .Timeout(TimeSpan.FromSeconds(0.5),
    //                            Observable.Empty<char>())
    //                    .Select(y => Tuple.Create(x, y))
    //    )
    //    .Switch()
    //    .Subscribe(observer);

    //Passes HOT. Passes Cold
    xs.Join(ys,
            _ => xs.Merge(ys),
            _ => Observable.Empty<Unit>(),
            Tuple.Create)
        .Subscribe(observer);

    testScheduler.Start();

    //You may want to Console.WriteLine out the two collections to validate for yourself.

    CollectionAssert.AreEqual(expected, observer.Messages);
}
[测试]
public void应仅从您处获取最新的值,而不是在您生产时获取()
{
//            11111111112222222222333
//   12345678901234567890123456789012
//x--x--x--x--x--x--x--x-
//      \           \       \
//y---y--------y---y---y-------
//       |           |       |
//rs----x----x----x-------
//y y y
var testScheduler=newtestscheduler();
//            11111111112222222222333
//   12345678901234567890123456789012
//x--x--x--x--x--x--x--x-
var xs=testScheduler.CreateColdObservable(
新记录(3,Notification.CreateOnNext('1')),
新记录(7,Notification.CreateOnNext('2')),
新记录(10,Notification.CreateOnNext('3')),
新记录(15,Notification.CreateOnNext('4')),
新记录(23,Notification.CreateOnNext('5')),
新记录(27,Notification.CreateOnNext('6')),
新记录(31,Notification.CreateOnNext('7'));
//            11111111112222222222333
//   12345678901234567890123456789012
//y---y--------y---y---y-------
var ys=testScheduler.CreateColdObservable(
新记录(5,Notification.CreateOnNext('A')),
新记录(17,Notification.CreateOnNext('B')),
新记录(21,Notification.CreateOnNext('C')),
新记录(25,Notification.CreateOnNext('D'));
//预期:
//勾选x y
//51A
//17.4 B
//25.5天
预期风险值=新[]
{
新录制的(5,Notification.CreateOnNext(Tuple.Create('1','A')),
新录制(17,Notification.CreateOnNext(Tuple.Create('4','B')),
新录制(25,Notification.CreateOnNext(Tuple.Create('5','D'))
};
var observer=testScheduler.CreateObserver();
//热通过,冷失败。由于超时,无法满足要求。
//xs.Select(x=>ys.Take(1)
//.超时(时间跨度从秒(0.5),
//Observable.Empty())
//.Select(y=>Tuple.Create(x,y))
//    )
//.Switch()
//.签署(观察员);
//传热传冷
xs.Join(ys,
_=>xs.Merge(ys),
_=>Observable.Empty(),
Tuple.Create)
.签署(观察员);
testScheduler.Start();
//您可能希望Console.WriteLine列出这两个集合,以便自己进行验证。
CollectionAssert.AreEqual(预期为observer.Messages);
}

顺便说一句,Merge的用法非常好。

这些事件的时间安排不规则,所以我不能使用超时。但是我可以合并ys和xs,取(1),然后过滤掉从xs中得到两个值的情况。这并不漂亮,我需要向xs和ys添加类型信息,但它应该可以工作。其工作方式类似于窗口。我不知道我是否可以用它来做些更优雅的事情。@oillio:我建议你把它作为你问题的答案:)嘿,我因为超时而投票否决了。在我看来,这不是正确的答案。@LeeCampbell你是对的。出于某种原因,我最初读到的问题是,y必须接近x。经进一步审查,没有此类计时要求,因此超时不正确。请使用
反应测试中的
OnNext
方法,避免重复
新记录的