How to convert RxJS triple 'of(...).pipe.map(...)' expression to the Observable of three dimensional array

Viewed 150

I have this expression:

var s2 = of(6, 16, 26).pipe(
    map(v1=>of(5, 15, 25).pipe(
      map(v2=>[v1,v2])
      )), 
    combineLatestAll());
  s2.subscribe(console.log)

... and s2 is an of type Observable<number[][]>as I want it.

Can I convert the following expression in a similar way to Observable<number[][][]> (instead of it being an Observable<Observable<number[]>[]>):

var r2 = of(5, 15, 25).pipe(
    map(v1=>of(6, 16, 26).pipe(
      map(v2=>of(7, 17, 27).pipe(
        map(v3 => [v1,v2,v3])
        )))),
    combineLatestAll());

   //Print the output:
    r2.subscribe(e1 => {
  e1.forEach(e2 => {
    e2.subscribe(e3 => {
      console.log(e3);
    });
  })
});

I can't get my head around it. Any help appreciated.

Solution suggested by Ruth:

let s3: Observable<number> = of(5, 15, 25);
let s4: Observable<number> = of(6, 16, 26);
let s5: Observable<number> = of(7, 17, 27);

const r1 = s3.pipe(
  map((v3: number) =>
    s4.pipe(
      mergeMap((v4: number) =>
        s5.pipe(
          reduce((acc: number[][], v5: number, i5: number) => {
            acc[i5] = [v3, v4, v5];
            return acc;
          }, [])
        )
      )
    )
  ),
  combineLatestAll()//reduce((acc: number[][], curr: number[][]) => [...acc, curr], [])
)
r1.subscribe(console.log);
3 Answers

It seems like you could use just toArray() operator but it's pretty hard to wrap my head around the nested calls so I'm not sure this is what you wanted :):

var r2 = of(5, 15, 25).pipe(
  map(v1=>of(6, 16, 26).pipe(
    map(v2=>of(7, 17, 27).pipe(
      map(v3 => [v1,v2,v3]),
      toArray(),
  )))),
  combineLatestAll());

Live demo: https://stackblitz.com/edit/rxjs-veofem?devtoolsheight=60

... or if you want to avoid the nested Observable you'll need mergeMap:


var r2 = of(5, 15, 25).pipe(
  map(v1=>of(6, 16, 26).pipe(
    mergeMap(v2=>of(7, 17, 27).pipe(
      map(v3 => [v1,v2,v3]),
      toArray(),
      )))),
  combineLatestAll());

r2.subscribe(e1 => {
  e1.forEach(e2 => {
    console.log(e2);
  })
});

Live demo: https://stackblitz.com/edit/rxjs-riquzh?devtoolsheight=60

something like below, your combineLatestAll() only works for the first level map that's why it returns the wrong result. I prefer mannually control the execution of observable with mergeMap,switchMap etc

of(5, 15, 25)
  .pipe(
    mergeMap((v1) =>
      of(6, 16, 26).pipe(
        mergeMap((v2) => of(7, 17, 27).pipe(map((v3) => [v1, v2, v3])
        ))
      )
    )
  )
  .subscribe(console.log);

Based on your comment, IMO this is rather a relatively difficult exercise to learn RxJS. Nevertheless, in addition to the solution provided by @martin, you could try to use the reduce with concatMap operator to manually set the array.

Note that reduce((acc, curr) => [...acc, curr], []) is synonymous to the toArray() operator.

const { of } = rxjs;
const { concatMap, reduce } = rxjs.operators;

const jsonEditor = document.getElementById('json-editor');
const textContainer = document.getElementById('text-container');

const options = {};
const editor = new JSONEditor(jsonEditor, options);

let s3 = of(5, 15, 25);
let s4 = of(6, 16, 26);
let s5 = of(7, 17, 27);

s3.pipe(
  concatMap((v3) =>
    s4.pipe(
      concatMap((v4) =>
        s5.pipe(
          reduce((acc, v5, i5) => {
            acc[i5] = [v3, v4, v5];
            return acc;
          }, [])
        )
      )
    )
  ),
  reduce((acc, curr) => [...acc, curr], [])
).subscribe({
  next: (response) => editor.set(response),
  error: (error) => textContainer.innerHTML = error
});
<link href="https://unpkg.com/jsoneditor@9.4.1/dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.5.5/rxjs.umd.min.js"></script>
<script src="https://unpkg.com/jsoneditor@9.4.1/dist/jsoneditor-minimalist.min.js"></script>

<div id="json-editor" style="width: 100%; height: 100%;"></div>
<span id="text-container"></span>

Update: Typescript

import { of, Observable } from 'rxjs';
import { concatMap, reduce } from 'rxjs';

let s3: Observable<number> = of(5, 15, 25);
let s4: Observable<number> = of(6, 16, 26);
let s5: Observable<number> = of(7, 17, 27);

s3.pipe(
  concatMap((v3: number) =>
    s4.pipe(
      concatMap((v4: number) =>
        s5.pipe(
          reduce((acc: number[][], v5: number, i5: number) => {
            acc[i5] = [v3, v4, v5];
            return acc;
          }, [])
        )
      )
    )
  ),
  reduce((acc: number[][], curr: number[][]) => [...acc, curr], [])
).subscribe(console.log);

Working example: Stackblitz

Related