我正试图找到一种基于另一个observable的值来过滤observable的方法。例如,假设我们只想在时间x和y之间接收事件。可以根据计时器的值过滤一个observable吗?
答案 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
。您还可以轻松扩展此解决方案以支持多个窗口(例如,使用开放时间流和关闭时间流工厂)。