How to properly merge several API calls that return observables into a single interface array?

Viewed 279

Back-End holds several databases to whom I need to make API calls on to gather a dataset that I require to display the information. Im using angular 12 and NGRX for state management.

API Requests are cast on to observables.

I'm trying to combine several observable arrays into a single observable array since I don't have an idea of how to pass in multiple objects to a selector and a reducer

So far I'm able to get the data and combine it but the output of getUtilities() instead of an observable array that I could pass to a selector I get several objects that the selctor does not how to deal with.

Redux shows that the action of successfull retreiving the data is executed 4 times. Redux showing that successful load action is triggered 4 times

My question is - How do I merge the API data into a single utiltiesList array without the need to call the backend several hundred times.

data.service.ts file contains all the API call functions

  getActiveProperties(): Observable<Property[]> {
    return this.http.get<Property[]>(this.getPropertiesUrl).pipe(
      mergeMap((data) => {
        return of(data);
      })
    );
  }

  getCurrentUtilities(
    currentMonth: string,
    currentYear: string
  ): Observable<utilities[]> {
    return this.http
      .get<utilities[]>(
        `${this.getUtiltiesUrl}?month=${currentMonth}&year=${currentYear}`
      )
      .pipe(
        mergeMap((data) => {
          return of(data);
        })
      );
  }

getPreviousUtilities(
    previousMonth: string,
    previousYear: string
  ): Observable<utilitiesPrevious[]> {
    return this.http
      .get<utilities[]>(
        `${this.getUtiltiesUrl}?month=${previousMonth}&year=${previousYear}`
      )
      .pipe(
        map((response) =>
          response.map(
            (sp): utilitiesPrevious => ({
              propertyID: sp.propertyID,
              cold1Prev: sp.cold1,
              cold2Prev: sp.cold2,
              hot1Prev: sp.hot1,
              hot2Prev: sp.hot2,
              gasPrev: sp.gas,
              electricityPrev: sp.electricity,
            })
          )
        )
      );

  getUtilityComments(): Observable<utilitiesComments[]> {
    return this.http.get<utilitiesComments[]>(`${this.getCommentsUrl}`).pipe(
      mergeMap((data) => {
        return of(data);
      })
    );
  }

To combine the data I've created this "beautiful" piece of code:

getUtilities(
    currentMonth: string,
    currentYear: string,
    previousMonth: string,
    previousYear: string
  ): Observable<utilitiesList[]> {
    const properties = this.getActiveProperties();
    const currentUtilities = this.getCurrentUtilities(
      currentMonth,
      currentYear
    );
    const previousUtilities = this.getPreviousUtilities(
      previousMonth,
      previousYear
    );
     const comments = this.getUtilityComments();

    return forkJoin([properties, currentUtilities, previousUtilities, comments])
      .pipe(
        switchMap((response) => [
          ...response[0]
            .map((item1) => ({
              ...response[1].find(
                (item2) => item2.propertyID === item1.propertyID && item2
              ),
              ...item1,
            }))
            .map((item1) => ({
              ...response[2].find(
                (item2) => item2.propertyID === item1.propertyID && item2
              ),
              ...item1,
            }))
            .map((item1) => ({
              ...response[3].find(
                (item2) => item2.propertyID === item1.propertyID && item2
              ),
              ...item1,
            })),
        ])
      )
      .pipe(map((data: unknown) => <utilitiesList[]>data));
  }

Here is a sample set of what the API Calls are returning: getActiveProperties()

[
  {
    "propertyID": 1,
    "AddressL1": "add1",
    "AddressL2": "cityA",
    "zip": "XXYYYY"
  },
  {
    "propertyID": 2,
    "AddressL1": "add2",
    "AddressL2": "cityB",
    "zip": "XXYYYY"
  },
  {
    "propertyID": 3,
    "AddressL1": "add3",
    "AddressL2": "cityC",
    "zip": "XXYYYY"
  },
  {
    "propertyID": 4,
    "AddressL1": "add4",
    "AddressL2": "cityD",
    "zip": "XXYYYY"
  }
]

getCurrentUtilities()

[
  {
    "utilityID": 77,
    "cold1": 169,
    "cold2": 145,
    "hot1": 153,
    "hot2": 153,
    "gas": 143,
    "electricity": 994,
    "month": 8,
    "year": 2021,
    "propertyID": 4,
    "commentID": 4
  }
]

getPreviousUtilities()

  {
    "utilityID": 19,
    "cold1": 165,
    "cold2": 167,
    "hot1": 158,
    "hot2": 158,
    "gas": 187,
    "electricity": 1015,
    "month": 7,
    "year": 2021,
    "propertyID": 1,
    "commentID": 1
  },
  {
    "utilityID": 38,
    "cold1": 170,
    "cold2": 162,
    "hot1": 154,
    "hot2": 155,
    "gas": 119,
    "electricity": 1082,
    "month": 7,
    "year": 2021,
    "propertyID": 2,
    "commentID": 2
  },
  {
    "utilityID": 57,
    "cold1": 148,
    "cold2": 176,
    "hot1": 175,
    "hot2": 139,
    "gas": 166,
    "electricity": 1065,
    "month": 7,
    "year": 2021,
    "propertyID": 3,
    "commentID": 3
  },
  {
    "utilityID": 76,
    "cold1": 165,
    "cold2": 142,
    "hot1": 149,
    "hot2": 149,
    "gas": 135,
    "electricity": 969,
    "month": 7,
    "year": 2021,
    "propertyID": 4,
    "commentID": 4
  }
]

getUtilityComments

[
  {
    "propertyID": 1,
    "Cold1Com": "Nr.6918",
    "Cold2Com": "Nr.5773",
    "Hot1Com": "Nr.7719",
    "Hot2Com": "Nr.5880",
    "GasCom": "Nr.2427",
    "ElectCom": "Nr.2744",
    "Comment": "Comm1"
  },
  {
    "propertyID": 2,
    "Cold1Com": "Nr.8573",
    "Cold2Com": "Nr.9652",
    "Hot1Com": "Nr.1279",
    "Hot2Com": "Nr.3600",
    "GasCom": "Nr.1730",
    "ElectCom": "Nr.1865",
    "Comment": "Comm2"
  },
  {
    "propertyID": 3,
    "Cold1Com": "Nr.6214",
    "Cold2Com": "Nr.9956",
    "Hot1Com": "Nr.8015",
    "Hot2Com": "Nr.8445",
    "GasCom": "Nr.4348",
    "ElectCom": "Nr.4714",
    "Comment": "Comm3"
  },
  {
    "propertyID": 4,
    "Cold1Com": "Nr.5625",
    "Cold2Com": "Nr.7039",
    "Hot1Com": "Nr.6681",
    "Hot2Com": "Nr.8978",
    "GasCom": "Nr.7224",
    "ElectCom": "Nr.6826",
    "Comment": "Comm4"
  }
]

All of the API requests have a common field - propertyID which is a number. Here's the list of interfaces for the respective objects:

export interface utilities {
  utilityID: number;
  propertyID: number;
  cold1: number | null;
  cold2: number | null;
  hot1: number | null;
  hot2: number | null;
  gas: number | null;
  electricity: number | null;

}

export interface utilitiesPrevious {

  cold1Prev: number | null;
  cold2Prev: number | null;
  hot1Prev: number | null;
  hot2Prev: number | null;
  gasPrev: number | null;
  electricityPrev: number | null;
  propertyID: number;
}

export interface utilitiesComments {
  propertyID: number;
  Cold1Com: string | null;
  Cold2Com: string | null;
  Hot1Com: string | null;
  Hot2Com: string | null;
  GasCom: string | null;
  ElectCom: string | null;
  Comment: string | null;
}

export interface utilitiesList {
  utilityID: number;
  propertyID: number;

  cold1: number | null;
  cold2: number | null;
  hot1: number | null;
  hot2: number | null;
  gas: number | null;
  electricity: number | null;

  cold1Prev: number | null;
  cold2Prev: number | null;
  hot1Prev: number | null;
  hot2Prev: number | null;
  gasPrev: number | null;
  electricityPrev: number | null;

  Cold1Com: string | null;
  Cold2Com: string | null;
  Hot1Com: string | null;
  Hot2Com: string | null;
  GasCom: string | null;
  ElectCom: string | null;
  Comment: string | null;

  AddressL1: string | null;
  AddressL2: string | null;
  zip: string | null;
  Tennant_ID: number | null;
  Lease_ID: number | null;
  Previous_Leases: number | null;
  Sold: number | null;
  Date_Sold: Date | null;
}

2 Answers

The map operator should be used instead of switchMap within the getUtilities method.

switchMap operator maps to an observable, not to an array.

Changing the operator from switchMap to map did the trick, thank you so much!

This is the function that returns the desired output:

 getUtilities(
    currentMonth: string,
    currentYear: string,
    previousMonth: string,
    previousYear: string
  ): Observable<utilitiesList[]> {
    const properties = this.getActiveProperties();
    const currentUtilities = this.getCurrentUtilities(
      currentMonth,
      currentYear
    );
    const previousUtilities = this.getPreviousUtilities(
      previousMonth,
      previousYear
    );
    const comments = this.getUtilityComments();

    return forkJoin([properties, currentUtilities, previousUtilities, comments])
      .pipe(
        map((response) => [
          ...response[0]
            .map((item1) => ({
              ...response[1].find(
                (item2) => item2.propertyID === item1.propertyID && item2
              ),
              ...item1,
            }))
            .map((item1) => ({
              ...response[2].find(
                (item2) => item2.propertyID === item1.propertyID && item2
              ),
              ...item1,
            }))
            .map((item1) => ({
              ...response[3].find(
                (item2) => item2.propertyID === item1.propertyID && item2
              ),
              ...item1,
            })),
        ])
      )
      .pipe(map((data: unknown) => <utilitiesList[]>data));
  }
Related