Rx.Net Group加入两个具有连接条件时间的Observable

时间:2018-01-03 10:08:54

标签: system.reactive rx.net

给定2个热点可观察量t1和t2我将如何获得GoupJoin以便从t2中获得所有事件,这些事件发生在t1中每个事件之后x秒和之后的y秒?

假设:

t1 ----- A ----- B ----- C

t2 --1--2--3--4--5--6

如果t1相隔2秒且t2相隔1秒,我们正在寻找每个t1事件两侧1秒的t2事件,结果如下。

结果:

{A,[1,2,3]}

{B,[3,4,5]}

{C,[5,6]}

以下是真实的例子,我们需要针对上述问题的解决方案。 我们有一组电子邮件和另一组短信。我们需要发出另一个流的结果,其中包含电子邮件,并且在电子邮件发送时间的1分钟之前或之后发生文本消息。

2 个答案:

答案 0 :(得分:1)

代码转储答案(使用100毫秒代替1秒):

var t1 = Observable.Interval(TimeSpan.FromMilliseconds(200))
    .Select(l => (char)('A' + l))
    .Delay(TimeSpan.FromMilliseconds(200));
var t2 = Observable.Interval(TimeSpan.FromMilliseconds(100))
    .Delay(TimeSpan.FromMilliseconds(100));

var x = TimeSpan.FromMilliseconds(100);     //before time
var y = TimeSpan.FromMilliseconds(100);     //after time

var g = t1.Timestamp().Join(t2.Timestamp(),
    c => Observable.Timer(y),
    i => Observable.Timer(x + y),
    (c, i) => new {GroupItem = c, RightItem = i}
)
    .Where(a =>
        (a.GroupItem.Timestamp > a.RightItem.Timestamp && a.GroupItem.Timestamp - a.RightItem.Timestamp <= x) //group-item came first
        || (a.GroupItem.Timestamp <= a.RightItem.Timestamp && a.RightItem.Timestamp - a.GroupItem.Timestamp <= y) // right-item came first, or exact timestamp match
    )
    .Select(a => new { GroupItem = a.GroupItem.Value, RightItem = a.RightItem.Value })
    .GroupBy(a => a.GroupItem, a => a.RightItem);

说明: Join完全是关于“windows”的。因此,当您定义连接时,您必须考虑左侧可观察和右侧可观察的每个项目的打开时间窗口。我们这里的窗口很难弄清楚:我们必须以某种方式打开一个窗口,在它发生之前左侧可观察的X时间,然后在它发生之后关闭它。

而不是做不可能的事情,所以我们让它只在左项目发生后打开Y时间,并让右项目窗口由X + Y时间定义。但是,这将为我们留下不应包含的项目。因此,我们在时间戳上使用Where来过滤掉这些内容。

最后,我们选择匿名类型和时间戳并将它们组合在一起。

我不认为GroupJoin是这样的方式:你最终会拆开小组并重新组合它,类似于我所做的......

答案 1 :(得分:1)

这里的问题(如Shlomo所提到的)是我们需要在发生t2事件之前在t1中打开窗口。不幸的是,这是不可能的,因为一旦我们在t1中达到了这个事件,我们已经超越了我们需要在t2打开窗口的时间点。

我们可以做的是使用Delay()及时转移t2。如果我们将其偏移x(之前的时间),我们可以将问题重新设置为“t2中的t1中的事件,并在t1 + x + y开始关闭var scheduler = new HistoricalScheduler(); var t1 = Observable.Interval(TimeSpan.FromMilliseconds(200), scheduler) .Select(l => (char)('A' + l)); var t2 = Observable.Interval(TimeSpan.FromMilliseconds(100), scheduler); var x = TimeSpan.FromMilliseconds(100); //before time var y = TimeSpan.FromMilliseconds(100); //after time var delayedT2 = t2.Delay(x, scheduler); var g = t1.GroupJoin(delayedT2 , _ => Observable.Timer(x + y, scheduler), _ => Observable.Empty<Unit>(scheduler), (a, b) => new { a, b} ); scheduler.Start(); 我们可以使用GroupJoin来解决这个问题。

{ A, [1,2] }
{ B, [3,4] }
{ C, [5,6] }

这给出了结果:

t2

这个结果仍然不是你所期待的。这是因为在您的示例中,t1事件发生在完全相同的即时t1 + y事件中。在这种情况下,首先处理t2事件并关闭窗口,然后才能包含(t1-01:00) <= t1 < (t1 + 01:00)事件。这意味着我们有效地获得了A。例如。 3的窗口是01:0000 - 02.9999 ...这就是为什么不包括{03}在03:00发生的原因。

只需在我们的y时间内添加一个勾号

,就可以将其修改为包含在内的
var y = TimeSpan.FromMilliseconds(100).Add(TimeSpan.FromTicks(1));