RxJS: Transforming an Observable array prior to subscribing

I am retrieving data (students through getStudents()) from an API that returns an Observable. Within this result, I need to obtain data from two different tables and merge the information.

Below are my simplified interfaces:

export interface student Student {
   id: string
   name: string,
   school_id: string,
   classroom_id: string
}

export interface school School {
   id: string,
   name: string
}

export interface classroom Classroom {
   id: string,
   name: string
}

My task now is to fetch all students and add the corresponding school and classroom for each student using the foreign keys school_id and classroom_id.

Currently, my approach resembles the following code snippet. It is incomplete as I am struggling to find the appropriate operator and its correct usage.

this.getStudents().pipe(
   switchMap(students => {
      student.map(student => forkJoin([
         this.getSchool(student.school_id),
         this.getClassroom(student.classroom_id)
      ]))
   })
)

All of the methods (getStudents(), getSchool(), getClassroom()) return Observables. My objective is to receive an array of students with the respective school and classroom data after subscribing.

I understand how to achieve this when fetching a single student (e.g. with getStudent()) and then combining multiple streams using combineLatest. However, it differs when retrieving multiple students.

Thank you in advance!

Answer ā„–1

To achieve the desired outcome, it is necessary to utilize the forkJoin method on the observable array obtained from student.map(), while also employing map to transform the result into the specified object format.

const result$ = getStudents().pipe(
  switchMap((students: Student[]) =>
    forkJoin(students.map(student =>
      forkJoin([
        getSchool(student.school_id),
        getClassroom(student.classroom_id)
      ]).pipe(map(([school, classroom]) => ({
        student,
        school,
        classroom
      }))
    )
  ))
));

Answer ā„–2

To simplify the process, you can convert the original array result into a stream that emits each student individually, and then construct each record one by one. After building the object, you can transform it back into a single array using toArray(). This method of flattening a stream that emits arrays into one that emits individual elements is easier to manage due to reduced nesting.

this.getStudents().pipe(
  switchMap(students => students), // emit each element individually
  concatMap(student => // construct the student object.
    forkJoin({
      classRoom: this.getClassroom(student.classroom_id),
      school: this.getSchool(student.school_id),
      student: of(student)
    })
  ),
  toArray() // when all results are built, emit one array result.
);

This technique is most effective if getStudents() returns a single emission followed by completion. If the observable remains active, toArray() will not emit values as it only triggers upon completion of the source observable.

If the observable stays active but you only need the initial array emission, consider adding take(1) or first() at the beginning of the pipe.

Check out this StackBlitz for demonstration.

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

Setting up Tarui app to access configuration data

I am looking to save a Tauri app's user configuration in an external file. The TypeScript front end accomplishes this by: import {appConfigDir} from "tauri-apps/api/path"; ... await fetch(`${await appConfigDir()}symbol-sets.json`) { ... ...

How to retrieve the value instead of the key/ID in a Laravel controller?

I am extracting data from the database and displaying it on the invoice view page using the json_encode($items); function. When I try to insert the 'price' field into the database, only the id/key is being stored instead of the actual value. Any ...

What is the process for updating the header of the group column from "Group" to "my custom heading"?

In my project, I need to replace the header "Group" with "MyCustomHeading". I am implementing this change using treeData in Material UI. ...

What is the method of posting to PHP using JavaScript without utilizing Ajax?

My current code involves a drag and drop file uploader to PHP using ajax, but it seems that my web host does not support ajax. Is there an alternative method to achieve this without using ajax? Specifically, I am facing issues with the UploadFile functio ...

Guide to positioning a toolbar within an element when hovering over it in HTML with the help of JQuery

function attachMenuBar() { $('body').on('mouseover mouseout', '*:not(.printToolBar)', function (e) { if (this === e.target) { (e.type === 'mouseover' ? setMenuBox(e.target) : removeMenuBox(e.t ...

How does handleChange receive the value as an input?

Greetings! Currently, I am delving into the world of React and JavaScript. I am experimenting with a Table Component demo that can be found at the following link: https://codesandbox.io/s/hier2?file=/demo.js:5301-5317 In the demo, there is a function defi ...

The issue of process.server being undefined in Nuxt.js modules is causing compatibility problems

I've been troubleshooting an issue with a Nuxt.js module that should add a plugin only if process.server is true, but for some reason it's not working as expected. I attempted to debug the problem by logging process.server using a typescript modu ...

JavaScript prototypal inheritance concept

During my free time, I like to dabble in JavaScript, but Iā€™m currently struggling with this particular topic. var person = new Person("Bob", "Smith", 52); var teacher = new Teacher("Adam", "Greff", 209); function Humans(firstName, lastName) { this. ...

Ensuring Koa ctx.query is valid prior to invoking the handler function

I'm currently working on a basic route that looks like this: router.get('/twitter/tweets', async (ctx) => { const { limit, page, search } = ctx.query ctx.body = { tweets: await twitter.all(limit, page, search), } }) The issue I ...

Is there a way to change this coffeescript into js/jquery?

content = element.html() content = content.replace(/(#include(\s*&lt;.*&gt;)?)/gi, '<span>$1</span>') content = content.replace(/(main\(.*\))/gi, '<span>$1</span>') element.html(content) h ...

What is the best way to retrieve and showcase data in NextJs version 13 and beyond?

Being new to NextJS, my question may seem trivial but I'd appreciate your patience. Essentially, my goal is to fetch data from a database and display it on the page upon the initial render. To achieve this, I am utilizing the useEffect and useState ho ...

Exploring the Depths of Web Scraping: A Guide to Scraping Within a Scraped Website

I have successfully scraped data from a specific page, but now I need to follow another href link in order to gather more information for that particular item. The problem is, I am unsure of how to do this. Here is an excerpt of what I have accomplished s ...

Leverage Vue's ability to assign data from a parent component to

I am struggling to bind the data (inputData) from the parent component to my child component. I have checked my code multiple times but cannot find where the mistake is. MainApp.js let vm = new Vue({ el: "#app", components: { &ap ...

Error message "$injector:unpr" occurs in the run method of AngularJS after minification process

I've encountered an issue with angular-xeditable on my Angular app. While it functions properly in the development environment, I'm facing an error in production when all JS files are minified: Uncaught Error: [$injector:strictdi] http://errors. ...

Check for compatibility of overflow:scroll with mobile browsers

Is there an easy JavaScript method that works across different devices and libraries? I am looking to assign a class to the html element in order to enable scrollable containers on mobile devices when needed. I want to follow a similar approach to Modern ...

What is the recommended approach for embedding scripts within Angular templates?

I am working on an Angular SPA that consists of multiple tabs, each served by templates. Depending on the tab, different scripts (both local and CDN) are required. Therefore, I am looking for a way to load these scripts only when they are needed, which mea ...

After refreshing the page, vuex is encountering null values when using firebase.auth().onAuthStateChanged

Encountering an issue with Vuex and Firebase Authentication integration. When reloading the page, there is a delay in response from firebase.auth().onAuthStateChanged. I require an immediate response upon page reload without using router guards as seen in ...

What is the method for extracting JavaScript code as data from a script tag?

I have a file external (let's say bar.js) function qux() {} Then in my webpage, I include it using the script tag: <script type="text/javascript" src="bar.js"></script> I am looking for a way to retrieve the JavaScript code from within ...

pictures in photo display

Please check out my codepen project: html code: <body> <div class="thumbnails"> <a href="#"><img src="http://s30.postimg.org/4yboplkxd/dotty.jpg" width="100" height="100"></a> <a href="#"><img src="http:// ...

Step-by-step guide on integrating a JSON array fetched via Ajax from a Django/Python view with DataTable

Being a new developer, I am embarking on my first professional project using Django. My main challenge lies in loading data that I have extracted from the models.py into a DataTable within my view.py file. Below is the snippet of my code. Brief Overview ...