我知道JavaScriptArray上有includes
运算符。
因此,从另外两个数组中查找公共元素是没有问题的。(https://stackoverflow.com/a/1885569/11455650)
那么,在rxjs
上实现这一点的最佳方式是什么?
const obs1$ = from(["foo", "bar", "baz", "qux"]);
const obs2$ = from(["bar", "garply", "fred", "foo"];
// const commonIntersection$ = functions or operators...
// result must be ["foo", "bar"]
我认为有两种方法可以实现这一点。
哪一个是计算高效的?我如何使用rxjs
运算符实现这一点?
merge
两个数组,只发射第二个值(忽略第一个值(filter
每个元素
我想你希望每个排放都有一个运行的十字路口?如果是这样,您可以使用扫描运算符,也可以滚动您自己的特定交叉点操作符。
scan运算符类似于数组上的reduce方法。在这种情况下,对于每个元素(它是一个字符串数组(,都会返回交集。接下来的每一次发射都将对最后的结果起作用。
merge(from([["foo", "bar", "baz", "qux"], ["bar", "garply", "fred", "foo"]])).pipe(
map(x => x),
scan((acc, cur) => {
return (!acc)
? next
: acc.filter(x => cur.includes(x));
}, undefined as string[] | undefined),
).subscribe(x => console.log(x));
自定义操作符看起来会更干净,所以如果你多次需要它,那就去用吧!正如您所看到的,下面的逻辑与扫描版本基本相同。它在acc变量中跟踪运行交叉口状态,并使用current数组对其进行更新。
merge(from([["foo", "bar", "baz", "qux"], ["bar", "garply", "fred", "foo"]])).pipe(
map(x => x),
intersection(),
).subscribe(x => console.log(x));
/*...*/
function intersection<T>(): OperatorFunction<T[], T[]> {
return (observable: Observable<T[]>) => {
return new Observable<T[]>((subscriber) => {
let acc: T[] | undefined; // keeps track of the state.
const subscription = observable.subscribe({
next: (cur) => {
acc = (!acc)
? cur
: acc.filter(x => cur.includes(x));
subscriber.next(acc);
},
error: (err) => subscriber.error(err),
complete: () => subscriber.complete(),
});
return () => subscription.unsubscribe();
})
}
}
如果您只想要最后一个结果,则将scan运算符更改为reduce,或者在运算符版本的内部订阅的完整回调中仅发出acc的值。
import { forkJoin, from } from 'rxjs';
import { toArray } from 'rxjs/operators';
import { intersection } from 'lodash';
const obs1$ = from(['foo', 'bar', 'baz', 'qux']);
const obs2$ = from(['bar', 'garply', 'fred', 'foo']);
forkJoin([obs1$.pipe(toArray()), obs2$.pipe(toArray())]).subscribe(
([arr1, arr2]) => console.log(intersection(arr1, arr2))
);
请在此处尝试:https://stackblitz.com/edit/rxjs-6jdyzy?file=index.ts
这不是rxjs
使用的代码,而是使用JavaScript本机数组方法(过滤器,包括(。
它似乎比rxjs
运算符更快。
const intersectionArray = combineLatest({
firstArray: [["foo", "bar", "baz", "qux"]],
secondArray: [["bar", "garply", "fred", "foo"]],
}).pipe(
map((x) => x.firstArray.filter((value) => x.secondArray.includes(value)))
);
intersectionArray.subscribe(console.log);