Using RxJS switchMap in combination with toArray allows for seamless transformation

I'm encountering an issue with rxjs.

I have a function that is supposed to:

  • Take a list of group IDs, such as: of(['1', '2'])
  • Fetch the list of chats for each ID
  • Return a merged list of chats

However, when it reaches the toArray method, nothing happens and no result is produced.

Code

  get chats$(): Observable<Chat[]> {
    return of(['1', '2']).pipe(
      filter(groupIds => !!groupIds && groupIds.length > 0),
      switchMap(groupIds => groupIds),
      switchMap(groupId => getGroupChats(groupId)), // fetch list of chats for the group id
      toArray(),
      map(doubleList => {
        return ([] as Chat[]).concat(...doubleList); // merge chat lists
      })
    );
  }

I also tried the following approach:

get chats$(): Observable<Chat[]> {
    return of(['1', '2']).pipe(
      filter(groupIds => !!groupIds && groupIds.length > 0),
      map(groupIds =>
        groupIds.map(groupId => getGroupChats(groupId))
      ),
      switchMap(chatList$ =>
        forkJoin(chatList$).pipe(
          map(doubleList => {
            return ([] as Chat[]).concat(...doubleList);
          })
        )
      )
    );
}

Test

The test response indicates:

Error: Timeout - Async callback was not invoked within 5000ms

describe("WHEN: get chats$", () => {
  const CHAT_MOCK_1: Chat = {
    id: "1",
  };
  const CHAT_MOCK_2: Chat = {
    id: "2",
  };

  it("THEN: get chats$ should return chat list", (done) => {
    service.chats$
      .subscribe((data) => {
        expect(data.length).toEqual(2);
        expect(data[0]).toEqual(CHAT_MOCK_1);
        expect(data[1]).toEqual(CHAT_MOCK_2);
        done();
      })
      .unsubscribe();
  });
});

Answer №1

This snippet of code demonstrates how to process an array of IDs by fetching each result individually and then collecting them into a new array.

from([1, 2, 3, 4])
  .pipe(
    mergeMap(item => of(item * 10)), // make a request for each item or any observable
    toArray()
  ).subscribe(console.log);

Answer №2

After much trial and error, I have finally achieved success with the following approach:

  • By using Array.map, I transformed our group ids array into a list of observables, each observable containing the array of chats for that specific group.
  • I then utilized the forkJoin function to obtain the final emitted values from each observable within the generated Array.

Code

get chats$(): Observable<Chat[]> {
    return this.groupsIds$.pipe(
        skipUntil(this._groupsLoaded$),
        switchMap((ids) => {
            const chatsList: Observable<Chat[]>[] = ids.map((id) =>
                this.getGroupChats$(id)
            );

            return forkJoin([...chatsList]).pipe(
                map((list) => ([] as Chat[]).concat(...list))
            );
        })
    )
}

I still have some questions regarding why this current method works while previous versions did not. Any clarification on this matter would be greatly appreciated.

To summarize: avoid concatenating multiple instances of switchMap.

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

Utilize the power of relative import by including the complete filename

When working on my TypeScript project, I have noticed that to import ./foo/index.ts, there are four different ways to do it: import Foo from './foo' // ❌ import Foo from './foo/index' // ❌ import Foo from './foo/i ...

I am curious about the types of props for the methods within the 'components' object in react-markdown

Having some trouble using 'react-markdown' in NextJs 13 with typescript. TypeScript is showing errors related to the props of the 'code' method, and after searching online, I found a solution that involves importing 'CodeProps&apos ...

Is it possible to access the service and 'self' directly from the HTML template?

When working with Angular 6, one method to access component properties from a service is to pass 'self' to the service directly from the component. An example of this implementation is shown below: myComponent.ts public myButton; constructor(p ...

The Typescript error "Attempting to call a function that does not have any callable signatures.(2349)"

Could you please assist me in resolving this issue: type IValidator = (value?: string) => string | undefined; type IComposeValidators = (validators: ((value?: string) => string | undefined)[]) => IValidator; export const composeValidators: ICompo ...

Creating Custom Type Guards for Literal Types in Typescript: Is It Possible?

Note: I am new to using typescript. Before asking this question, I made sure to go through the documentation on advanced types and type guards. Additionally, I looked into several related questions on Stack Overflow such as user defined type guards [typesc ...

What is the process for creating documentation for a TypeScript enum type with the format of { [key]: value }

I am currently developing a logger service for nodeJS using Typescript. One important component of this project is an enum that looks like this: enum LOG_TYPES { NONE = 0, ERROR = 1, WARN = 2, INFO = 3, DEBUG = 4, } Along with the enum, I have i ...

Dealing with errors in Next.js 13 with middleware: a comprehensive guide

My attempt to manage exceptions in Next.js 13 using middleware is not producing the desired results. Below is my current code: import { NextRequest, NextFetchEvent, NextResponse } from "next/server" export function middleware(req: NextRequest, e ...

Utilize Primeng data grid to calculate total sum and display it in the footer section

I have been utilizing primeng datatable in a recent project and am currently facing an issue with calculating the sum in the footer of a row grouping DataTable. The summation needs to occur while data is being edited and new data is being entered. Below i ...

How can one effectively outline the structure of a document within firestore?

Currently, I am enclosing my calls to Firebase within a function so that I can specify the return type within the function. This allows me to define the type of data being retrieved from a document. However, TypeScript complains if you do not convert the F ...

Variations in comparing tuple types in TypeScript

Exploring the TypeScript Challenge, there is a particular problem known as IsNever. The task at hand is to create a type called IsNever that takes input of type T. If the resolved type equates to never, the output should be true; otherwise, it should be fa ...

Encountering the "RequestDevice() chooser has been cancelled by the user" error when using Electron 17.x with Web Bluetooth

After reviewing the following StackOverflow resources: Web Bluetooth & Chrome Extension: User cancelled the requestDevice() chooser Electron Web Bluetooth API requestDevice() Error Can you manipulate web bluetooth chooser that shows after calling requestD ...

What is the proper way to manage the (ion select) OK Button?

Hey there, I'm working with an Ionic select directive and I need to customize the functionality of the 'OK' button. When I click on it, I want it to call a specific function. I'm aware of the (ionChange) event, but that only triggers w ...

CSS: Achieving Full Container Width Text with Respect to Another Container's Height

Hello, I am facing a very specific issue that I can't seem to resolve: I have two containers (ion-card) and I want them to always be the same height. I was able to achieve this by following the solution provided here: https://i.sstatic.net/he8O7.png ...

Unable to execute NPM AUDIT FIX

We are facing a challenge with a Windows PC that has been rebuilt. After successfully cloning the project we were working on, it now refuses to build or compile. The project was an Angular 7 build and everything was running smoothly with NVM installed and ...

Ensuring Data Consistency: Using TypeScript to Strongly Type Arrays with Mixed Variable Types

I have a JSON array that may contain objects of two types, defined by IPerson and ICompany. [ { "Name" : "Bob", "Age" : 50, "Address": "New Jersey"}, { "Name" : "AB ...

What are some examples of utilizing paths within the tsconfig.json file?

Exploring the concept of path-mapping within the tsconfig.json file led me to the idea of utilizing it to streamline cumbersome path references: The project layout is unconventional due to its placement in a mono-repository that houses various projects an ...

I'm unable to modify the text within my child component - what's the reason behind this limitation?

I created a Single File Component to display something, here is the code <template> <el-link type="primary" @click="test()" >{{this.contentShow}}</el-link> </template> <script lang="ts"> imp ...

Do not include ChangeDetectionStrategy when creating component

Is it possible to eliminate the default ChangeDetectionStrategy for each component creation? (Please note that I am working with Angular V 10 in a controlled environment for project maintenance) @Component({ xyz, changeDetection: ChangeDetectionStrategy. ...

Encountering difficulties while attempting to set up ngx-doc-viewer in Angular 9

Having trouble installing ngx-doc-viewer in my Angular 9 project. Encountered some errors, here they are attached. Has anyone dealt with these issues before? or Recommend any other document viewer compatible with Angular 9. npm ERR! code ERESOLVE npm E ...

Changing the position of the custom tooltip in Ag-grid

Is there a way to adjust the placement of my custom tooltip? I want it to always appear in the upper right corner of the table, rather than below the cursor. Here is an example of how it currently displays: pic1 I have a specific image of how I would like ...