Tips for merging an Observable containing a series of values with an Observable containing a single value and subsequently simplifying this arrangement

I am searching for the most efficient method to combine two functions that return Observables.

The initial function is described as:

abstract getProfiles(): Observable<ClientProfile[]>

The other one is:

abstract getUser(userId: number): Observable<User>

My goal is to create a data structure projection like this:

interface Projection {
  user: User,
  profiles: ClientProfile[]
}

To achieve this, I am looking to optimize the following function:

getUserProfiles() {
  this.userService.getProfiles()
    .pipe(
      flatMap((ps: ClientProfile[]) => {
         ps.map(p => this.userService.getUser(p.userId))
         // I NEED TO RETURN AN OBSERVABLE HERE:
         // {user: u, ...ps}
      })
    )
    .subscribe(_ => doSomething(_))

What is the most effective way to merge a single user with the list of profiles?

Answer №1

To improve efficiency, consider using a combination of the mergeMap and forkJoin operators.

import {map as rxMap, mergeMap} from 'rxjs/operators';
import {forkJoin} from 'rxjs';

this.userService.getProfiles().pipe(
    mergeMap(profiles =>
        forkJoin(profiles.map(profile =>
            this.userService.getUser(profile.userId).pipe(
                rxMap(user =>
                    ({
                        user,
                        profiles
                    })
                )
            )
        )
    )
)

The profiles list is transformed into an

Array<Observable<{user, profile}>>
. These observables complete when the http request returns from the userService.

ForkJoin waits for these observables to finish before emitting an observable that contains the completed values. MergeMap combines these inner observables created by getUser() with the outer observable from getProfiles().

Answer №2

One approach is to use switchMap to generate an array of Observables from the getUser() API calls:

getUserProfiles() {
    this.userService.getProfiles()
      .pipe(
        switchMap((profiles: ClientProfile[]) => {
           const observables = profiles.map(profile => {
              return this.userService.getUser(profile.userId)
                         .pipe(
                           map(user => ({user, profile: profiles}))
                         );
           });

           return observables;
           // Here you should return an Observable:
           // {user: u, ...profiles}
        }),
        switchMap(response => response)
      )
      .subscribe(data => processUserData(data))

This solution is suitable if you prefer processing each emitted getUser() API response without waiting for all requests to be completed.

If you need to wait for all getUser() API calls to finish before further processing, you can utilize forkJoin() as shown below:

getUserProfiles() {
    this.userService.getProfiles()
      .pipe(
        switchMap((profiles: ClientProfile[]) => {
           const observables = profiles.map(profile => {
              return this.userService.getUser(profile.userId)
                         .pipe(
                           map(user => ({user, profile: profiles}))
                         );
           });

           return forkJoin(observables);
           // Here you should return an Observable:
           // {user: u, ...profiles}
        }),
        tap(result => //result will be an array of {user, profile})
      )
      .subscribe(data => processUserData(data))

I hope this explanation proves helpful.

Answer №3

Feel free to use this solution:

function solve() {
    interface ProfileInfo {
        id: number, name: string, userId: number
    }
    interface UserInfo {
        id: number, username: string
    }

    const fetchProfiles = () => ajax('api/profiles').pipe(
        pluck<AjaxResponse, Array<ProfileInfo>>('response')
    );

    const fetchUser = (id: number) => ajax(`api/users/${id}`).pipe(
        pluck<AjaxResponse, UserInfo>('response')
    );

    fetchProfiles().pipe(
        concatMap(profiles => from(profiles).pipe(map(p => [p, profiles]))),
        mergeMap(([p, profiles]: [ProfileInfo, ProfileInfo[]]) => 
                        fetchUser(p.userId).pipe(map(u => [u, profiles])))
    ).subscribe(console.log);
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The parameter of type '{ [key:string]:any;}' cannot be assigned a string argument

When attempting to attach a click event handler on a TypeScript file, I encountered the following error message: Argument of type 'string' is not assignable to parameter of type '{ [key:string]:any;} https://i.sstatic.net/ATkz0.png The ...

The functionality of Angular 2's AsyncPipe seems to be limited when working with an Observable

Encountering an Error: EXCEPTION: Unable to find a support object '[object Object]' in [files | async in Images@1:9] Here's the relevant part of the code snippet: <img *ngFor="#file of files | async" [src]="file.path"> Shown is the ...

Using Angular to Make a Request for a Twitter API Access Token

I'm facing some challenges while trying to implement a Twitter Sign-In method for my angular app. The issue seems to be with the initial step itself. I am attempting to make a post request to the request_token API by following the steps outlined at th ...

Issue encountered when trying to integrate Angular2 with Visual Studio 2015 Update

Starting my journey with Angular2 in Visual Studio 2015. Following advice from this helpful article. Encountering an issue when running the index.html file, it gets stuck on 'Loading...'. Here are the key configurations and code files being use ...

In Angular, dynamically updating ApexCharts series daily for real-time data visualization

I am currently working with apexchart and struggling to figure out how to properly utilize the updateseries feature. I have attempted to directly input the values but facing difficulties. HTML <apx-chart [chart]="{ type: ...

Issues with Vercel deployment/building are occurring. The error message states: "Failed to compile. Type error: Unable to locate module ... or its associated type declarations

I'm attempting to launch my initial Next.js application using Vercel. Even though the app runs smoothly on my local machine (it builds locally with yarn run build and I can develop normally using yarn run dev), I am encountering issues with the build ...

Troubleshooting issue: Unable to Subscribe to Subject in Angular 5+ with RxJS

My service has an observable that is subscribed to, and the payload is passed to a subject in the service. However, when I subscribe to the subject in my component, nothing happens. I expect to see the output from console.log in the .subscribe method withi ...

TypeScript seems to be failing to detect the necessary checks before they are used

I've been pondering on how to ensure TypeScript acknowledges that I am verifying the existence of my variables before using them. Below is the code snippet : Here's the function responsible for these checks: function verifyEnvVars(){ if (!proc ...

What are the appropriate scenarios to utilize the declare keyword in TypeScript?

What is the necessity of using declare in TypeScript for declaring variables and functions, and when is it not required? For instance, why use declare var foo: number; when let foo: number; seems to achieve the same result (declaring a variable named ...

The issue arises in ts-jest nestjs where there is an attempt to access properties of an undefined

Creating an application using NestJS and utilizing ts-jest for e2e testing. For the code repository, visit: https://github.com/redplane/jest-issue A controller is set up with the following structure: @Controller({ path: 'api/shape', scope: S ...

Angular 2 rc4's HTTP requests are generating uncaught exceptions (in promise)

Is there a change in Angular 2 HTTP service after upgrading to Angular 2 rc4? I have spent the entire day going through Angular HTTP client documentation and searching on Stack Overflow, but I can't seem to find the solution to my problem. Here are ...

Retrieve the dimensions of the image in degrees from the component

I need to retrieve the dimensions of an image I uploaded along with its base64 code. Below is the code snippet I am using: Image img = new Image(); img.src = Selectedimage.src; Img.onload = function { this.width = img.width; this.height = img.height; } T ...

A special function designed to accept and return a specific type as its parameter and return value

I am attempting to develop a function that encapsulates a function with either the type GetStaticProps or GetServerSideProps, and returns a function of the same type wrapping the input function. The goal is for the wrapper to have knowledge of what it is ...

Conditional return type mistakes

I'm facing an issue with a function that takes a parameter "value" and is supposed to return 0 or 1 based on its true or false value. Check it out here. const f = <T extends boolean>(value: T): false extends T ? 0 : 1 => { if (value === ...

The promise chain from the ngbModal.open function is being bypassed

I'm currently working on implementing data editing within a component. My task involves checking if any of the data fields have been altered, and if so, prompting a confirmation pop-up to appear. If the user confirms the change, the data will then be ...

Mastering the key property in React is imperative for optimized performance and

Typically, I find it easy to understand how to use the key property. const test = [1,2,3,4,5]; return ( <> {test.map(x => <div key={x.toString()}>{x}</div>)} </> ); However, when my map function is structured lik ...

react-query: The 'isSuccess' property types do not match

i have the following code snippet: import { get } from "lodash-es" import {useQuery} from '@tanstack/react-query' type QueryResult<TData extends object> = | { isSuccess: false isPending: true isE ...

Type property is necessary for all actions to be identified

My issue seems to be related to the error message "Actions must have a type property". It appears that the problem lies with my RegisterSuccess action, but after searching on SO, I discovered that it could be due to how I am invoking it. I've tried so ...

Is there a way to program a function that can automatically trigger or refresh an HTTP POST method?

How can I create a method in a distant component that will run a POST request when a button is clicked? I believe I need to use a service in this situation. It's not necessary for it(this.qwe) to be in the constructor, it's just an example... ...

Compiling TypeScript to JavaScript with Deno

Currently experimenting with Deno projects and looking for a way to transpile TypeScript into JavaScript to execute in the browser (given that TS is not supported directly). In my previous experience with NodeJS, I relied on installing the tsc compiler via ...