如何创建一个在两个可观察对象之间交替的可观察对象

时间:2016-02-02 06:28:08

标签: c# system.reactive

如果我有可观察的ab,我想根据第三个可观察的c在两者之间交替,我将如何做到这一点?大理石图看起来像这样:

a: -a1---------a2----a3--a4------a5--a6-----a7----
b: -b1--b2----b3--b4----------------b5---b6---b7--
c: --------c-----------c--------c----------c------
                    alternate
   -a1--------b3--b4-----a4---------b5---b6-a7----

3 个答案:

答案 0 :(得分:3)

这很好用:

var query =
    a.Publish(pa =>
        b.Publish(pb =>
            c.StartWith("c")
                .Select((x, n) => n % 2 == 0 ? pa : pb)
                .Switch()));

我使用此代码进行了测试:

var xs = new []
{
    "a1", "b1", "b2", "c", "b3",
    "a2", "b4", "a3", "c", "a4",
    "c", "a5", "b5", "a6", "b6",
    "c", "a7", "b7",
}
    .ToObservable()
    .Publish();

var a = xs.Where(x => x.StartsWith("a"));
var b = xs.Where(x => x.StartsWith("b"));
var c = xs.Where(x => x.StartsWith("c"));

var query = ...

query.Subscribe(Console.WriteLine);

xs.Connect();

我得到了这个结果:

a1 
b3 
b4 
a4 
b5 
b6 
a7 

xs.Connect()才能使xs可观察的工作成为生成三个序列的简便方法。

甚至作为扩展方法:

public static IObservable<TSource> Alternate<TSource>(
    this IObservable<TSource> leftSelector,
    bool startLeft,
    IObservable<TSource> left,
    IObservable<TSource> right)
{
    return
        left.Publish(pl =>
            right.Publish(pr =>
                leftSelector.StartWith(default(TSource))
                    .Select((x, n) => (n % 2 == (startLeft ? 0 : 1)) ? pl : pr)
                    .Switch()));
}

答案 1 :(得分:2)

这是另一种方式,类似的想法 - 它还允许您指定选择器的起始值。

关键的想法是使用ZipMostRecent使用最新的选择器值压缩每个值流。然后我们可以根据选择器值过滤每个压缩流并合并它们。

public static IObservable<TSource> Alternate<TSource>(
    this IObservable<bool> leftSelector,
    bool startLeft,
    IObservable<TSource> left,
    IObservable<TSource> right)
{   
    return leftSelector.Publish(
    selector => 
      Observable.Merge(        
        left.Zip(selector.MostRecent(startLeft), Tuple.Create)
            .Where(l => l.Item2),                      
        right.Zip(selector.MostRecent(startLeft), Tuple.Create)
             .Where(r => !r.Item2)))
      .Select(res => res.Item1);            
}

此外,这里的Enigmativity的整洁方法略微调整,以适应这里的输入:

public static IObservable<TSource> Alternate2<TSource>(
    this IObservable<bool> leftSelector,
    bool startLeft,
    IObservable<TSource> left,
    IObservable<TSource> right)
{   
    return
        left.Publish(l =>
            right.Publish(r =>
                leftSelector.StartWith(startLeft)
                    .Select(s => s ? l : r)
                    .Switch()));        
}  

答案 2 :(得分:1)

此解决方案将c声明为Observable<bool>

结果Observable根据c的最新值从a和b发出值。 a和b中的值包装在Merge

的容器类中
var a = Observable.Range(1, 10);
var b = Observable.Range(10, 20);

var merged = a.Select(i => new Container {id = "a", value = i})
              .Merge( b.Select(i => new Container {id = "b", value = i}));

var c = Observable.Return(true); 

var result = merged.CombineLatest( c , (ab, selector ) 
                  => (selector && ab.id == "a") || (!selector && ab.id == "b") ? ab : null)
                   .Where(i => i != null);

public class Container
{
    public string id;
    public int value;
}