C# Rx顺序分组(分区流)

C# Rx顺序分组(分区流),c#,reactive-programming,system.reactive,C#,Reactive Programming,System.reactive,我有一系列事件: event.EventTime: 1s-----2s----3s----4s----5s----6s--- stream: A-B-C--D-----------------E-F---G-H-- 事件如下所示: public class Event { public DateTime EventTime { get; set; } public int Value { get; set; } } event.EventTime: 1s-----2

我有一系列事件:

event.EventTime: 1s-----2s----3s----4s----5s----6s---
stream:          A-B-C--D-----------------E-F---G-H--
事件如下所示:

public class Event
{
  public DateTime EventTime { get; set; }
  public int Value { get; set; }
}
event.EventTime: 1s-----2s----3s----4s----5s----6s---
stream:          A---C--D-------------------F-----H--
EventTime
应与事件到达的时间相对应,但可能会有一点延迟。不过,这些活动不应该无序进行

现在,当我指定一个分组间隔(比如1秒)时,我希望流按如下方式分组

1s-------2s----3s----4s----5s-----6s---
[A-B-C]--[D]---[ ]---[ ]---[E-F]--[G-H]
(注意空间隔)

我尝试过使用
缓冲区
,但遗憾的是,我需要按EventTime进行分区,而不是按System.DateTime.Now进行分区。即使使用边界,我也需要某种前瞻性,因为当我使用缓冲区(2,1)作为边界并比较[0]和[1]时,即使[1]成功地破坏了缓冲区,它仍然会被插入到旧的缓冲区而不是新的缓冲区中。我还尝试了
GroupBy
,但只有在输入流完成后才能生成组。这是不应该发生的。然后我试了一些这个东西:

var intervalStart = GetIntervalStartLocal(DateTime.Now) + intervalLength;
var intervals = Observable.Timer(intervalStart, intervalLength);
var eventsAsObservables = intervals.GroupJoin<long, Event, long, Event, (DateTime, IObservable<Event>)>(
        data,
        _ => Observable.Never<long>(),
        _ => Observable.Never<Event>(),
        (intervalNumber, events) => {
          var currentIntervalStart = intervalStart + intervalNumber*intervalLength;
          var eventsInInterval = events
            .SkipWhile(e => GetIntervalStartLocal(e.EventTime) < currentIntervalStart)
            .TakeWhile(e => GetIntervalStartLocal(e.EventTime) == currentIntervalStart);
          return (currentIntervalStart, eventsInInterval);
        });

      var eventsForIntervalsAsObservables = eventsAsObservables.SelectMany(g => {
        var lists = g.Item2.Aggregate(new List<Event>(), (es, e) => { es.Add(e); return es; });
        return lists.Select(l => (intervalStart: g.Item1, events: l));
      });

      var task = eventsForIntervalsAsObservables.ForEachAsync(es => System.Console.WriteLine(
        $"=[{es.intervalStart.TimeOfDay}]= " + string.Join("; ", es.events.Select(e => e.EventTime.TimeOfDay))));

await task;
然后构造(正确地说,根据它的操作):


我想我一定是做错了什么,因为基于流事件值对流进行分区应该不会那么困难。

您需要澄清您想要什么。鉴于此:

time  : 1s-------2s----3s----4s----5s-----6s---
stream: A-B-C----D-----------------E-F----G-H-- (actual)
group : [A-B-C]--[D]---[ ]---[ ]---[E-F]--[G-H] (desired result)
不清楚这里的“时间”是您的活动时间戳还是实际时间。如果是实际时间,那么这当然是不可能的:在C到达之前,你不能通过ABC列表。如果您指的是您的事件时间戳,那么
缓冲区
或者
窗口
将必须知道何时停止,这并不是那么容易做到的

GroupBy
对我的作用如下:

var sampleSource = Observable.Interval(TimeSpan.FromMilliseconds(400))
    .Timestamp()
    .Select(t => new Event { EventTime = t.Timestamp.DateTime, Value = (int)t.Value });

sampleSource
    .GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
    .Dump(); //LinqPad
唯一的问题是,每个组都没有一个严格的标准,所以这是一个巨大的内存泄漏。因此,您可以添加计时器来关闭组:

sampleSource
    .GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
    .Select(g => g.TakeUntil(Observable.Timer(TimeSpan.FromSeconds(2)))) //group closes 2 seconds after opening
    .Dump(); //LinqPad
此结束操作还允许我们返回带有
.ToList()
的列表,而不是可观察的列表:

sampleSource
    .GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
    .SelectMany(g => g.TakeUntil(Observable.Timer(TimeSpan.FromSeconds(2))).ToList())
    .Dump(); //LinqPad
为什么
.Window(…)
不适合您?
sampleSource
    .GroupBy(e => e.EventTime.Ticks / 10000000) //10M ticks per second
    .SelectMany(g => g.TakeUntil(Observable.Timer(TimeSpan.FromSeconds(2))).ToList())
    .Dump(); //LinqPad