这是我尝试绘制大理石图 -
STREAM 1 = A----B----C---------D------>
(magical operator)
STREAM 2 = 1----------2-----3-----4--->
STREAM 3 = 1A---------2C----3C----4D-->
我基本上在寻找从流1和流2生成流3的东西。基本上,每当从流2发出某些东西时,它就会将它与流1中的最新内容相结合。combineLatest
类似于我想要的但是我只想要从流2发出的东西从流2发出,而不是流1。这样的运营商是否存在?
答案 0 :(得分:5)
有一个运算符可以满足您的需要:sample
的一个重载需要另一个可观察而不是持续时间作为参数。文档在这里:https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#sample-or-throttlelast
用法(我将在scala中给出示例):
import rx.lang.scala.Observable
import scala.concurrent.duration
import duration._
def o = Observable.interval(100.milli)
def sampler = Observable.interval(180.milli)
// Often, you just need the sampled observable
o.sample(sampler).take(10).subscribe(x ⇒ println(x + ", "))
Thread.sleep(2000)
// or, as for your use case
o.combineLatest(sampler).sample(sampler).take(10).subscribe(x ⇒ println(x + ", "))
Thread.sleep(2000)
输出:
0,
2,
4,
6,
7,
9,
11,
13,
15,
16,
(2,0),
(4,1),
(6,2),
(7,3),
(9,4),
(11,5),
(13,6),
(15,7),
(16,8),
(18,9),
有一个轻微的问题,即吞下了来自采样的observable的重复条目(参见https://github.com/ReactiveX/RxJava/issues/912的讨论)。除此之外,我认为这正是你所寻找的。 p>
答案 1 :(得分:3)
据我所知,现有的单一运营商不会做你想做的事情。但是,您可以使用CombineLatest
和DistinctUntilChanged
进行撰写,如下所示:
var joined = Observable.CombineLatest(sourceA, sourceB, (a,b) => new { A = a, B = b })
.DistinctUntilChanged(pair => pair.B);
编辑:
只要STREAM 1的值每次都改变,上述操作就会起作用。如果他们不这样做,那么请使用以下内容,但不太清楚,但适用于所有情况(我已经测试过)。
var joined = Observable.Join(
sourceB,
sourceA,
_ => Observable.Return(Unit.Default),
_ => sourceA,
(a, b) => new { A = a, B = b });
Join
运算符对我来说永远不会直观,我发现的最佳解释是here。
回答@ Matthew的评论
var buttonClicks = Observable.FromEventPattern<MouseButtonEventArgs>(this,
"MouseLeftButtonDown")
.Select(_ => Unit.Default);
var sequence = Observable.Interval(TimeSpan.FromSeconds(1));
var joined = Observable.Join(
buttonClicks,
sequence,
_ => Observable.Return(Unit.Default),
_ => sequence,
(b, s) => s); // No info in button click here
答案 2 :(得分:3)
这是一个相当简单的方法:
var query = stream2.Zip(
stream1.MostRecent(' '),
(s2,s1) => string.Format("{0}{1}", s2, s1));
MostRecent
可以提供“零”值,该值在事件流1尚未发出时使用。对于引用类型,这可能为null,但我为stream1使用了char
,因此提供了一个空格。
答案 3 :(得分:2)
withLatestFrom
似乎完全符合我的要求 - http://rxmarbles.com/#withLatestFrom
答案 4 :(得分:0)
我认为Switch
运算符是关键所在。
试试这个:
var query =
stream1
.Select(s1 => stream2.Select(s2 => new { s1, s2 }))
.Switch();
以下测试代码:
query
.Select(s => String.Format("{0}{1}", s.s2, s.s1))
.Subscribe(Console.WriteLine);
stream1.OnNext('A');
stream2.OnNext(1);
stream1.OnNext('B');
stream1.OnNext('C');
stream2.OnNext(2);
stream2.OnNext(3);
stream1.OnNext('D');
stream2.OnNext(4);
给出了这些结果:
1A
2C
3C
4D
如果这是正确的,请告诉我。
答案 5 :(得分:0)
解决方案
public static IObservable<TR> Sample<TSource, TSampler, TR>
(this IObservable<TSource> source,
IObservable<TSampler> sampler,
Func<TSource, TSampler, TR> combiner)
{
return source.Publish
(rs => sampler
.Zip
( rs.MostRecent(default(TSource))
, (samplerElement, sourceElement)
=> combiner(sourceElement, samplerElement)
)
.SkipUntil(rs)
);
}
有一个测试用例,因为这样的事情很难做到。
public class SampleSpec : ReactiveTest
{
TestScheduler _Scheduler = new TestScheduler();
[Fact]
public void ShouldWork()
{
var sampler = _Scheduler.CreateColdObservable
( OnNext(10, "A")
, OnNext(20, "B")
, OnNext(30, "C")
, OnNext(40, "D")
, OnNext(50, "E")
, OnNext(60, "F")
);
var source = _Scheduler.CreateColdObservable
( Enumerable
.Range(5,100)
.Where(i=>i%10!=0)
.Select(i=>OnNext(i,i)).ToArray());
var sampled = source.Sample
(sampler, Tuple.Create);
var actual = _Scheduler.Start
(() =>
sampled
, created: 0
, subscribed: 1
, disposed: 1000);
actual.Messages.Count()
.Should()
.Be(6);
var messages = actual.Messages.Take(6)
.Select(v => v.Value.Value)
.ToList();
messages[0].Should().Be(Tuple.Create(9,"A"));
messages[1].Should().Be(Tuple.Create(19,"B"));
messages[2].Should().Be(Tuple.Create(29, "C"));
messages[3].Should().Be(Tuple.Create(39, "D"));
messages[4].Should().Be(Tuple.Create(49, "E"));
messages[5].Should().Be(Tuple.Create(59, "F"));
}
}