如何根据分组方法将永不结束的流拆分为多个结束流?
--a--a-a-a-a-b---b-b--b-c-c---c-c-d-d-d-e...>
进入这些可观察者
--a--a-a-a-a-|
b---b-b--b-|
c-c---c-c-|
d-d-d-|
e...>
如您所见,a
位于开头,收到b
后,我将不再获得a
,因此应该结束。这就是正常groupBy
不好的原因。
答案 0 :(得分:5)
您可以使用{Obnerable}的window
和share
。 bufferCount(2, 1)
还有一个小技巧:
const str = 'a-a-a-a-a-b-b-b-b-c-c-c-c-d-d-d-e';
const source = Observable.from(str.split('-'), Rx.Scheduler.async).share();
source
.bufferCount(2, 1) // delay emission by one item
.map(arr => arr[0])
.window(source
.bufferCount(2, 1) // keep the previous and current item
.filter(([oldValue, newValue]) => oldValue !== newValue)
)
.concatMap(obs => obs.toArray())
.subscribe(console.log);
这会打印(因为toArray()
):
[ 'a', 'a', 'a', 'a', 'a' ]
[ 'b', 'b', 'b', 'b' ]
[ 'c', 'c', 'c', 'c' ]
[ 'd', 'd', 'd' ]
[ 'e' ]
此解决方案的问题是订阅source
的顺序。我们需要window
通知程序在第一个bufferCount
之前订阅。否则,首先进一步推送项目,然后检查它是否与前一个.filter(([oldValue, newValue]) ...)
不同。
这意味着需要在window
之前将发射延迟一个(这是第一个.bufferCount(2, 1).map(arr => arr[0])
。
或者使用publish()
const str = 'a-a-a-a-a-b-b-b-b-c-c-c-c-d-d-d-e';
const source = Observable.from(str.split('-'), Rx.Scheduler.async).share();
const connectable = source.publish();
connectable
.window(source
.bufferCount(2, 1) // keep the previous and current item
.filter(([oldValue, newValue]) => oldValue !== newValue)
)
.concatMap(obs => obs.toArray())
.subscribe(console.log);
connectable.connect();
输出相同。
答案 1 :(得分:2)
也许某人可以提出更简单的东西,但这有效(小提琴:https://fiddle.jshell.net/uk01njgc/)......
let counter = 0;
let items = Rx.Observable.interval(1000)
.map(value => Math.floor(value / 3))
.publish();
let distinct = items.distinctUntilChanged()
.publish();
distinct
.map(value => {
return items
.startWith(value)
.takeUntil(distinct);
})
.subscribe(obs => {
let obsIndex = counter++;
console.log('New observable');
obs.subscribe(
value => {
console.log(obsIndex.toString() + ': ' + value.toString());
},
err => console.log(err),
() => console.log('Completed observable')
);
});
distinct.connect();
items.connect();
答案 2 :(得分:2)
这是一个变体,它为你包裹了所有的嫌疑人分享......
const stream = ...;
// an Observable<Observable<T>>
// each inner observable completes when the value changes
const split = Observable
.create(o => {
const connected = stream.publish();
// signals each time the values change (ignore the initial value)
const newWindowSignal = connected.distinctUntilChanged().skip(1);
// send the observables to our observer
connected.window(newWindowSignal).subscribe(o);
// now "start"
return connected.connect();
});
答案 3 :(得分:0)
import { from } from 'rxjs';
import { share, window, map, publish, switchMap,
skip, toArray, distinct, bufferCount }
from 'rxjs/operators';
export function splitToArray(){
const str = 'a-a-a-a-a-b-b-b-b-c-c-c-c-d-d-d-e-e';
const lettters = from(str.split('-'))
.pipe(
share(),
publish()
);
lettters
.pipe(
bufferCount(2, 1),
window(
lettters
.pipe(distinct(),
skip(1)
),
),
switchMap(obs => obs.pipe(
map(([val1, val2]) => {return val1;}),
toArray()))
)
.subscribe(console.log);
lettters.connect();
}
我对这个问题的看法。使用rxjs6管道函数。
这里的技巧是延迟@martin建议的元素 使用bufferCount(2,1)。因此,每个元素都会发射两次。除了第一个。 红色大箭头是创建新窗口时我们时间线上的时刻。之后,只需将数组映射为采用double数组的第一个元素即可。