我有两个对象流,每个对象都有一个Timestamp
值。两个流都是有序的,因此例如时间戳可能是一个流中的T a = 1,3,6,6,7
而另一个流中的T b = 1,2,5,5,6,8
。两个流中的对象属于同一类型。
我希望能够做的是按时间戳的顺序将每个事件放在总线上,即放置A 1 ,然后放置B 1 ,B 2 ,A 3 ,依此类推。此外,由于某些流具有多个具有相同时间戳的(顺序)元素,因此我希望将这些元素分组,以便每个新事件都是一个数组。所以我们将[A 3 ]放在总线上,然后是[A 1 5 ,A 2 < sub> 5 ]等等。
我试图通过创建两个ConcurrentQueue
结构来实现这一点,将每个事件放在队列的后面,然后查看队列的每个前端,首先选择先前的事件,然后遍历队列等所有具有此时间戳的事件都存在。
但是,我遇到了两个问题:
我认为Rx可以在这方面提供帮助,但我没有看到明显的组合使这成为可能。因此,非常感谢任何建议。
答案 0 :(得分:10)
Rx确实非常适合这个问题IMO。
由于显而易见的原因, IObservables
不能'OrderBy'(你必须首先观察整个流以保证正确的输出顺序),所以我的答案在下面作出假设(你说的)你的2源事件流是有序的。
最后这是一个有趣的问题。标准的Rx运算符缺少可以轻松解决这个问题的GroupByUntilChanged
,只要在观察到下一组的第一个元素时在前一个组上调用OnComplete
就可以观察到它。然而,查看DistinctUntilChanged
的实现,它不遵循这种模式,只在源observable完成时调用OnComplete
(即使它知道在第一个非不同元素之后将不再有元素。 .. 奇怪的???)。无论如何,出于这些原因,我决定不使用GroupByUntilChanged
方法(不破坏Rx约定)而是转而使用ToEnumerableUntilChanged
。
免责声明:这是我的第一个Rx扩展,所以我希望得到有关我的选择的反馈。此外,我的一个主要问题是匿名观察者持有distinctElements
列表。
首先,您的应用程序代码非常简单:
public class Event
{
public DateTime Timestamp { get; set; }
}
private IObservable<Event> eventStream1;
private IObservable<Event> eventStream2;
public IObservable<IEnumerable<Event>> CombineAndGroup()
{
return eventStream1.CombineLatest(eventStream2, (e1, e2) => e1.Timestamp < e2.Timestamp ? e1 : e2)
.ToEnumerableUntilChanged(e => e.Timestamp);
}
现在ToEnumerableUntilChanged
实施(代码墙警告):
public static IObservable<IEnumerable<TSource>> ToEnumerableUntilChanged<TSource,TKey>(this IObservable<TSource> source, Func<TSource,TKey> keySelector)
{
// TODO: Follow Rx conventions and create a superset overload that takes the IComparer as a parameter
var comparer = EqualityComparer<TKey>.Default;
return Observable.Create<IEnumerable<TSource>>(observer =>
{
var currentKey = default(TKey);
var hasCurrentKey = false;
var distinctElements = new List<TSource>();
return source.Subscribe((value =>
{
TKey elementKey;
try
{
elementKey = keySelector(value);
}
catch (Exception ex)
{
observer.OnError(ex);
return;
}
if (!hasCurrentKey)
{
hasCurrentKey = true;
currentKey = elementKey;
distinctElements.Add(value);
return;
}
bool keysMatch;
try
{
keysMatch = comparer.Equals(currentKey, elementKey);
}
catch (Exception ex)
{
observer.OnError(ex);
return;
}
if (keysMatch)
{
distinctElements.Add(value);
return;
}
observer.OnNext( distinctElements);
distinctElements.Clear();
distinctElements.Add(value);
currentKey = elementKey;
}), observer.OnError, () =>
{
if (distinctElements.Count > 0)
observer.OnNext(distinctElements);
observer.OnCompleted();
});
});
}