Rx java Rx:一个类似zip的操作符,在其中一个流结束后继续?

Rx java Rx:一个类似zip的操作符,在其中一个流结束后继续?,rx-java,rxjs,rx-py,rx.net,Rx Java,Rxjs,Rx Py,Rx.net,我希望将异步开始和结束的流(可观察对象)组合起来: -1----1----1----1---|-> -2----2--|-> [ optional_zip(sum) ] -1----3----3----1---|-> 我需要它的目的是:将音频流添加到一起。它们是音频“块”流,但我将在这里用整数表示它们。现在播放的是第一个片段: -1----1----1----1---|-> 然后第二个开始,稍晚一点: -2----2--|-> 按总和合并的

我希望将异步开始和结束的流(可观察对象)组合起来:

-1----1----1----1---|->
     -2----2--|->
[ optional_zip(sum) ]
-1----3----3----1---|->
我需要它的目的是:将音频流添加到一起。它们是音频“块”流,但我将在这里用整数表示它们。现在播放的是第一个片段:

-1----1----1----1---|->
然后第二个开始,稍晚一点:

     -2----2--|->
按总和合并的结果应为:

-1----3----3----1---|->
但是,如果任何压缩流结束,则标准压缩完成。我希望这个可选的_-zip即使其中一个流结束也能继续运行。在Rx中有没有办法做到这一点,或者我必须通过修改现有的Zip来实现它


注意:我使用的是RxPy,但这里的社区似乎很小,Rx运营商在不同语言之间似乎非常通用,所以我也将其标记为Rx java和Rx js。

因此我有一些代码可以工作,我认为它们可以满足您的大部分需求。基本上,我创建了一个函数
zipAndContinue
,它将像
zip
一样运行,只是只要一些底层流仍有数据要发送,它就会继续发送项目。该功能仅通过冷观测进行了[短暂]测试

此外,还欢迎进行更正/增强/编辑

function zipAndContinue() {
    // Augment each observable so it ends with null
    const observables = Array.prototype.slice.call(arguments, 0).map(x => endWithNull(x));
    const combined$ = Rx.Observable.combineLatest(observables);

    // The first item from the combined stream is our first 'zipped' item
    const first$ = combined$.first();

    // We calculate subsequent 'zipped' item by only grabbing
    // the items from the buffer that have all of the required updated
    // items (remember, combineLatest emits each time any of the streams
    // updates).
    const subsequent$ = combined$
        .skip(1)
        .bufferWithCount(arguments.length)
        .flatMap(zipped)
        .filter(xs => !xs.every(x => x === null));

    // We return the concatenation of these two streams
    return first$.concat(subsequent$)
}
下面是使用的实用功能:

function endWithNull(observable) {
    return Rx.Observable.create(observer => {
        return observable.subscribe({
            onNext: x => observer.onNext(x),
            onError: x => observer.onError(x),
            onCompleted: () => {
                observer.onNext(null);
                observer.onCompleted();
            }
        })
    })
}

function zipped(xs) {
    const nonNullCounts = xs.map(xs => xs.filter(x => x !== null).length);

    // The number of streams that are still emitting
    const stillEmitting = Math.max.apply(null, nonNullCounts);

    if (stillEmitting === 0) {
        return Rx.Observable.empty();
    }

    // Skip any intermittent results
    return Rx.Observable.from(xs).skip(stillEmitting - 1);
}
下面是示例用法:

const one$ = Rx.Observable.from([1, 2, 3, 4, 5, 6]);
const two$ = Rx.Observable.from(['one']);
const three$ = Rx.Observable.from(['a', 'b']);

zipAndContinue(one$, two$, three$)
    .subscribe(x => console.log(x));

// >> [ 1, 'one', 'a' ]
// >> [ 2, null, 'b' ]
// >> [ 3, null, null ]
// >> [ 4, null, null ]
// >> [ 5, null, null ]
// >> [ 6, null, null ]

这里有一个js提琴(您可以单击Run,然后打开控制台):

我将把它分成两部分来解决这个问题。首先,我想要一个能接受
可观察的
并产生
可观察的
的东西,其中数组只包含“活动”(即非完整)可观察的。任何时候一个新元素被添加到外部可观察物,任何时候一个内部可观察物完成,一个新的数组将被发射,包含适当的可观察物。这本质上是主流的“扫描”减少

一旦你有了可以做到这一点的东西,你就可以使用flatMapLatest和zip来得到你想要的东西

我在第一部分的基本尝试如下:

function active(ss$) {
    const activeStreams = new Rx.Subject();
    const elements = [];
    const subscriptions = [];

    ss$.subscribe(s => {
        var include = true;
        const subscription = s.subscribe(x => {}, x => {}, x => {
            include = false;
            const i = elements.indexOf(s);
            if (i > -1) {
                elements.splice(i, 1);
                activeStreams.onNext(elements.slice());
            }
        });

        if (include) {
            elements.push(s);
            subscriptions.push(subscription);
            activeStreams.onNext(elements.slice());
        }   
    });

    return Rx.Observable.using(        
        () => new Rx.Disposable(() => subscriptions.forEach(x => x.dispose())),
        () => activeStreams
    );
}
从这里开始,你只需拉上拉链,然后像这样把它压平:

const zipped = active(c$).flatMapLatest(x =>
    x.length === 0 ? Rx.Observable.never()
  : x.length === 1 ? x[0]
  : Rx.Observable.zip(x, (...args) => args.reduce((a, c) => a + c))
);
我假设零个活动流不应该产生任何东西,一个活动流应该产生它自己的元素,两个或更多的流应该压缩在一起(所有这些都反映在map应用程序中)

我的(承认相当有限的)测试将这种组合产生您想要的结果


顺便提一下,这个问题很好。我还没有看到任何解决问题第一部分的方法(虽然我不是Rx专家;如果有人知道已经有这种方法,请发布详细信息)。

你在找什么?CombineTest将继续使用流在结束前显示的最后一个值,以我为例,结果是-1---3---6---6---6-|->。我希望操作员在流结束时忽略它,只压缩其余的流。@uryga您能更好地解释一下,您想得到什么?如果您想使用总和选择器压缩元素,那么您的图表是错误的,结果将是-(1+2+3)-(1+2+3)-(1+3)-(1)——(1)————————————————————————————————————————————————————————————————————————————————————————————。所以,您是想按索引(如zip运算符)组合元素,还是按元素发出的时间组合元素?我从没想过会看到以null结尾的可观察对象;)哈哈,是的;有点讨厌。如果任何源观测值在完成之前发出null,那么这肯定会中断。不过,谢谢。我花了几天时间才弄明白如何编写自己的rx操作符,因为RxPy文档不存在。我将尝试一种稍微不同的方法,但受到你的启发。看看Matt Burnell的答案,我们在那里讨论了一种不同的方法。我有一个类似的想法,并且已经实现了你所说的第一个
select\u running
函数,但它并不漂亮:pastebin.com/NdXJZeJLas现在,每次没有当前正在运行的流时,它都会发布一个空数组,但如果
,则可以通过一个
来删除该数组。如果你愿意,我可以解释发生了什么,特别是因为那里有3个嵌套函数。。。另外,这也是RxPy实现的一半的样子。答案已更新。我看了一下您的RxPy实现,它在本质上与我的RxJs实现类似。我不知道如何使它更漂亮,因为你必须跟踪流和你自己对它们的订阅,以便进行适当的清理。不过,我并没有改变任何逃避函数的东西。我也考虑过使用一个主题,但我遵循了内置函数的实现方式——一个接收观察者的订阅函数。这样我就可以直接给观察者写信,而不需要中间主题。然后,我将该函数转换为一个匿名可观察函数,它只接受我提供的订阅函数,并将其应用于任何未来的订阅者。(解释这一点是因为我花了很多时间才真正弄清楚几乎没有文档记录的RxPy是如何工作的)是有道理的;我还不太熟悉Rx的内部结构,所以我只是使用“公共”结构编写它。我想你的方式快一点。不过,主题可以演示这个概念。