Ward Beullens
Ward Beullens

Reputation: 403

Filter an observable by an observable

Let's consider the following simplified situation:

I want to filter the apples observable such that the rotten apples don't pass the filter. More precisely, an apple A passes the filter if and only if the first item emitted by A.isRotten() is false. What is the best way to implement this filter?

After some thinking I could come up with this:

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

Which is written in javascript. ( ... => ... is a function). This works, but I think it is rather lengthy and difficult to understand. Is there a better way to do this kind of thing?

Upvotes: 8

Views: 4183

Answers (3)

Simon_Weaver
Simon_Weaver

Reputation: 145890

One way to achieve that is like this, sorry I didn't get to adapt this to fruit filtering:

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)));

}));

This filters 'paidFor' orders and emits a new array every time an order becomes paid or unpaid.

Note: If the isPaidFor$ observable can't change between searches then this whole exercise is pointless because there would be no reason to provide a 'live view' like this. This only makes sense if the observable can actually change between search results.

This could be extended to much more complicated rules if needed (such as adding filtering checkboxes) - that's why I created the intermediate visibilityRules array - which strictly speaking is just for readability.

Upvotes: 0

Richard Szalay
Richard Szalay

Reputation: 84734

What you've got is fine and, tbh, I can't think of a more concise way of doing it. I'd probably use flatMap rather than concatMap if out-of-order apples aren't an issue.

If readibility is an issue for you, just move the implementation into it's one function (eg. filterObservable that accepts a function that takes a value and returns an IObservable<bool>)

Upvotes: 4

Toan Nguyen
Toan Nguyen

Reputation: 11581

You can do something like this:

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));

Upvotes: -3

Related Questions