Home > Software engineering >  How to merge nested observables in a pipe rxjs
How to merge nested observables in a pipe rxjs

Time:12-18

In my Angular app I retrieve a list of post from an API call and, for each fo the posts, I have to do another API call to retrieve the number of likes.
I can't think of a clean way to do it, but current attempt of a solution is the following:

apiCall.pipe(
  switchMap((posts: Post[]) => {
    return posts.map((post: Post) => {
      return this.getPostLikes(post.rootMessage).pipe(
        map((likes: Like[]) => {
          post.rootMessage.reactions = likes;
          return post;
        }),
      );
    });
  }),
  mergeAll(),
  map((res: Post[]) => {
    return { res, currentPage: page   1 };
  }),
)

I feel like the entire approach is wrong, but I can't think at any other strategies.

CodePudding user response:

A tangent note: making everything an inline anonymous function makes code hard to read. How about more verbose, but easier to maintain style:

private getLikesForPostsArray = (posts: Post[]) => forkJoin({
  posts: of(posts),
  likes: forkJoin(posts.map((post: Post) => this.getPostLikes(post.rootMessage))),
});

private updateReactionsForPosts = ({posts, likes}: { posts: Post[], likes: Like[] }) =>
  posts.map((post: Post, index: number) => ({
    rootMessage: {...post.rootMessage, reactions: likes[index]},
    ...post,
  }));

//...

return apiCall.pipe(
  switchMap(this.getLikesForPostsArray),
  map(this.updateReactionsForPosts),
  map((res: Post[]) => ({
    res,
    currentPage: page   1 // I don't see where `page` is defined
  })),
);

CodePudding user response:

The solution depends on whether you want to make the requests in parallel or one after another. Your solution right now with mergeAll() will make all requests at the same time but I think you can make it more simple without mergeAll():

apiCall.pipe(
  switchMap((posts: Post[]) =>
    merge(posts.map(post => this.getPostLikes(post.rootMessage).pipe(
      map(likes => ({ likes, rootMessage: post.rootMessage})), // Just to be able to pair post with a response
    ))).pipe(,
      scan((acc, {likes, rootMessage}) => {
        const post = acc.find(p => p.rootMessage === response.rootMessage);
        post.rootMessage.reactions = likes;
        return acc;
      }, posts),
      last(), // Emit only once when all request have completed
    )
  }),
  • Related