按可观察对象过滤可观察对象

Filter an observable by an observable

让我们考虑以下简化情况:

我想过滤可观察到的苹果,这样烂苹果就不会通过过滤器。更准确地说,当且仅当 A.isRotten() 发出的第一项为假时,苹果 A 通过过滤器。实施此过滤器的最佳方式是什么?

经过一番思考,我想到了这个:

apples
    .concatMap(apple => 
        apple.isRotten()
            .first()
            .filter(bool => bool)
            .map(bool => apple))

里面写的是javascript。 ( ... => ... 是一个函数)。这行得通,但我认为它相当冗长且难以理解。有没有更好的方法来做这种事情?

你可以这样做:

var seq = Rx.Observable.from([1, 2, 3, 4, 5, 6])
    .filter(x => {
        let isRotten = true;
        Rx.Observable.just(x % 2 === 0)
            .first()
            .subscribe(val => isRotten = val);

        if (isRotten) {
            return x;
        }
    });

seq.subscribe(x => console.log(x));

你得到的很好,老实说,我想不出更简洁的方法了。如果乱序的苹果不是问题,我可能会使用 flatMap 而不是 concatMap

如果可读性对您来说是个问题,只需将实现移到它的一个函数中(例如,filterObservable 接受一个函数,该函数接受一个值和 returns 一个 IObservable<bool>

一种实现方式是这样的,抱歉我没能将它应用到水果过滤中:

const orders$: Observable<Order[]> = searchOrders(...);

const filteredOrders$ = orders$.pipe(switchMap((orders) => {

    // take all orders and determine visibility based on an observable
    const visibilityRules = orders.map(o => {

        return {                    
            order: o,
            visible$: o.isPaidFor$   // this is an observable on an Order object
        };
    });

    const filtered = visibilityRules.map(o => o.visible$.pipe(map(visible => visible ? o.order : undefined )));
    return (filtered.length == 0) ? of([]) : combineLatest(filtered).pipe(map(combined => combined.filter(x => x != undefined)));

}));

这会过滤 'paidFor' 订单并在每次订单已支付或未支付时发出一个新数组。

注意:如果 isPaidFor$ observable 不能在搜索之间改变,那么整个练习就毫无意义,因为没有理由提供这样的 'live view'。这只有在可观察值在搜索结果之间实际发生变化时才有意义。

如果需要,这可以扩展到更复杂的规则(例如添加过滤复选框)——这就是我创建中间 visibilityRules 数组的原因——严格来说,这只是为了提高可读性。