gka
gka

Reputation: 509

rxjs - combining inner observables after filtering

I call backend that respond with:

[
  "https://some-url.com/someData1.json",
  "https://some-url.com/someData2.json"
]

Each JSON can have following schema:

{
  "isValid": boolean,
  "data": string
}

I want to get array with all data, that have isValid is set to true

backend.get(url)
    .pipe(
        mergeMap((urls: []) =>
            urls.map((url: string) =>
                backend.get(url)
                    .pipe(
                        filter(response => response.isValid),
                        map(response => response.data)
                    )
            )
        ),
        combineAll()
    )

When both .json have "isValid" set to true, I get array with both data. But when one of them has "isValid" set to false observable never completes.

I could use mergeAll instead of combineAll, but then I receive stream of single data not collection of all data.

Is there any better way to filter out observable?

Upvotes: 1

Views: 3987

Answers (2)

Buggy
Buggy

Reputation: 3649

import { forkJoin, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

interface IRes {
  isValid: boolean;
  data: string;
}

interface IResValid {
  isValid: true;
  data: string;
}

function isValid(data: IRes): data is IResValid {
  return data.isValid;
}

const res1$: Observable<IRes> = backend.get(url1);
const res2$: Observable<IRes> = backend.get(url2);

// When all observables complete, emit the last emitted value from each.
forkJoin([res1$, res2$])
  .pipe(map((results: IRes[]) => results.filter(isValid)))
  .subscribe((results: IResValid[]) => console.log(results));

Upvotes: 0

ggradnig
ggradnig

Reputation: 14159

As you said, the inner observable never emits, because filter does not forward the only value that is ever emitted by the backend.get observable. In that case, the operator subscribing on that observable - in your case combineAll - will also never receive any value and cannot ever emit itself.

What I would do is just move the filtering and mapping to combineAll by providing a project function, like that:

backend.get(url)
    .pipe(
        mergeMap((urls: string[]) =>
            urls.map((url: string) => backend.get(url))
        ),
        combineAll(responses =>
            responses
                .filter(response => response.isValid)
                .map(response => response.data)
        )
    )

See if that works for you ;)

Upvotes: 1

Related Questions