mergeAll

Let's forget about observable for now, and revisit the data structure we use everyday: Array.

say, I have an array,

[1, 2, 3] // these are just numbers wrapped in a context, and this context happens to be Array.

and I can use map to transform the element into another element, for example I can multiple each element by 2

[1, 2, 3].map((x) => 2 * x)
// [2, 4, 6], so we have new numbers still in the context: Array

I can chain other methods on this context too, as these methods will be evaluated on the elements in the context

[1, 2, 3]
  .map((x) => 2 * x)
  .filter((x) => x > 4)

Here's the interesting part, what if the returned value is another context (another array)

[1, 2, 3].map((x) => [2 * x])

// [[2], [4], [3]]

Can I still chain filter after this map?

[1, 2, 3].map((x) => [2 * x])
  .filter((x) => x > 4) // what is x here?

The x here is actually the element which is another element in context(Array), aka, Higher Order Array(or Nested Array).

in order to use following chaining methods on the context, we need to extract the element inside the nested brackets. Javascript happens to have a method called flat to do this job

[1, 2, 3]
  .map((x) => [2 * x])
  .flat()

// [2, 4, 3]

And we can happily to filter again!

[1, 2, 3]
  .map((x) => [2 * x])
  .flat()
  .filter((x) => x > 4)

If you understand what flat does in Array, then you alredy knows the role of mergeAll.

In the context of Observable, we have a method called mergeAll equivalent to flat in the context of Array. It is usually used togher with map, where here map returns a new context(new Observable)

of(1, 2, 3).pipe(
  map((x) => of(2 * x)), // returns a new Observable
  mergeAll(),
  filter((x) => x > 4)
)

And since map + mergeAll is a so common combination, we have mergeMap to simplify operators.