如何使用IObservable <int>将IObservable <T>分段为不同长度的IObservable <T []>

时间:2019-07-03 19:51:55

标签: c# rx.net

我有一个“值” IObservable<T>,它返回T个元素,这些元素必须按顺序组合成可变长度数组,并且我有一个“控件” IObservable<int>,它告诉我下一个数组必须多长时间。 删除元素,重复元素或使结果混乱会使结果变得毫无意义。

这是我在Rx.NET中重写的串行连接机器人技术项目。

IObservable<char> values = new [] {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }.ToObservable();
IObservable<int> control = new [] { 1, 4, 2 }.ToObservable();
IObservable<char[]> result = control.SelectMany(length => values.Take(length).ToArray());

我想看到这样的东西:

values  ----A--B--C--D--E--F--G--H-->
control --1-----4---------------2--->
result  ---[A]---------[BCDE]--[FG]->

但是到目前为止,我的尝试导致了

-[A]-[AB]-[ABCD]->

3 个答案:

答案 0 :(得分:1)

好的,这是适合我所有需求的代码。普罗格曼,您在根据您的建议进行这项工作中发挥了作用。在这里,它被包裹在整洁的Observable.Create中,并变成了IObservable<T>上的扩展方法,并且使用了一次性方法来处理压缩序列上的预订。

    public static IObservable<T[]> Chop<T>(this IObservable<T> values, IObservable<int> control) =>
        Observable.Create<T[]>(observer => 
        {
            List<T> buffer = new List<T>();
            return values.Zip(control.SelectMany(length => Enumerable.Repeat(length, length)), 
                              (value, length) => (value, length))
                         .Subscribe(next => 
                         {
                             buffer.Add(next.value);
                             if (buffer.Count == next.length)
                             {
                                 observer.OnNext(buffer.ToArray());
                                 buffer.Clear();
                             }
                         });
        });

示例输出:

values  ----A--B--C--D--E--F--G--H--I--J--K--L--M--N--O--P-->
control --1-4-2-0-3-3--------------------------------------->
result  ---[A]---------[BCDE]-[FG]----[HIJ]----[KLM]-------->

答案 1 :(得分:0)

您可以创建几个辅助主题,以准备/创建新的可观察对象以构建所需的新可观察对象。您可以为这些可观察对象建立主题:

  1. 创建一个新的observable,它重复一个唯一值,该唯一值的数量等于从control读取的数字。从(1, 4, 2)您将获得(guid_1, guid_2, guid_2, guid_2, guid_2, guid_3, guid_3)。将此称为可观察的repeatSize
  2. 使用Zip()运算符组合来自repeatSizevalues的一个值。您将获得以下值的观察值:((A,guid_1), (B,guid_2), (C,guid_2), (D,guid_2), (E,guid_2), (F,guid_3), (G,guid_3))。将此称为可观察的zippedValues
  3. 订阅zippedValues并将原始值添加/添加到列表中。还保存来自repeatSize可观察值的先前值。将其与repeatSize的当前值进行比较。更改完后(例如从guid_2更改为guid_3),您知道您已经到达结束/开始位置,因此可以将填充列表发送到新的可观察对象。之后,您可以重新设置列表,然后再次开始填写。

您可能需要构建2-3个Subject<T>对象,对其进行订阅,并使用多个OnNext()调用来从其他可观察对象的订阅中填充它们。

答案 2 :(得分:0)

我还没有对此进行测试,但是我相信您可以结合使用Zip()Scan()来产生所需的输出。

IObservable<char> values = new [] {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }.ToObservable();
IObservable<int> control = new [] { 1, 4, 2 }.ToObservable();
IObservable<char[]> result = control.Zip(
    control.Scan(values, (rest, length) => rest.Skip(length)),
    (length, vals) => vals.Take(length).ToArray()
);