如何根据另一个的值过滤Observable?

时间:2013-12-26 21:30:29

标签: c# filter system.reactive observable reactive-extensions-js

我正试图找到一种基于另一个observable的值来过滤observable的方法。例如,假设我们只想在时间x和y之间接收事件。可以根据计时器的值过滤一个observable吗?

2 个答案:

答案 0 :(得分:3)

有几种方法。没有一些代码,很难知道哪个最好。

通过CombineLatest(一直听,只根据最新值过滤):

var astream = ...;
var bstream = ...;
var filtered = Observable.CombineLatest(astream, bstream, (a, b) => new { a, b })
          .Where(v => v.b >= x && v.b <= y)
          .Select(v => v.a); // Alas sometimes you will get duplicate a's.

通过选择和切换(当astream满足某些条件时,只收听bstream):

var astream = ...;
var bstream = ...;
var filtered = bstream
    .Select(b => (b >= x && b <= y) ? astream : Observable.Empty<T>())
    .Switch();

答案 1 :(得分:1)

Brandon'所述,有很多方法可以组合事件流。

Observable.Join的反应性联接是一个非常通用的工具,但Rx内置运算符库的很大一部分组合流的方式可以支持基于另一个的过滤。

我非常喜欢布兰登的Select + Switch技术(+1来自我);我把它归档以备将来使用!

这是一种直接解决将源流过滤到开始和结束时间的问题的方法。它比Select + Switch有一些优势,包括:

  • 它可以避免检查源流的每个事件的过滤条件。
  • 避免需要“空”流。
  • 它只订阅一次来源。
  • 只要到达结束时间,它就会发送OnCompleted(),而不是在源流发送时持续发送。

它真的归结为Observable.Window运算符的特定重载,但我将逐步解释它。

基本思想是通过应用在开始时间打开并在结束时关闭的Window来过滤源流。

首先让我们创建一秒脉冲的示例源流(xs),以及开始时间和结束时间:

var xs = Observable.Interval(TimeSpan.FromSeconds(1));

var startTime = DateTime.Now + TimeSpan.FromSeconds(5);    
var endTime = DateTime.Now + TimeSpan.FromSeconds(8);

为简洁起见,我并未检查startTime是否在endTime之前。现在我们创建一个用于打开窗口的流,以及一个用于关闭窗口的流:

var start = Observable.Timer(startTime);
var end = Observable.Timer(endTime);

最后使用Observable.Window过滤源流。此运算符的输出是流的流(IObservable<IObservable<T>>) - 每个子流都是一个新窗口。

我们将使用的重载接受一个事件标记新窗口打开的流,以及一个工厂函数,用于在触发窗口打开的事件的情况下提供关闭流。

使用我们的计时器流,我们知道在开始时间只会创建一个窗口,并在结束时关闭。

我们使用Observable.Merge

来平整流的流
var filtered = xs.Window(start, _ => end).Merge();

如果我们这样订阅:

filtered.Subscribe(Console.WriteLine);

我们按预期获得以下输出:

4
5
6

同样,有很多种方法可以解决这个问题,而不仅仅是使用Window。您还可以轻松扩展此解决方案以支持多个窗口(例如,使用开放时间流和关闭时间流工厂)。