uglycode
uglycode

Reputation: 3082

RXjs - flatMap then "merge" it again

I am learning RXjs and I want to understand how things work. So that's why I envisioned a scenario where I make Observable.of and pass in an array. I know, Observable.from would be more appropriate, but I'm learning how things work.

So, here's my code:

let sourceZero = Rx.Observable
                    .of([{name: 'James', age: 25}, {name: 'Angelina', age: 30}]);

sourceZero.subscribe(
  x =>  console.log(x)
);

This works as expected: the subscribed value is array.

OK, so now I want to filter that array. While I know there are many other choices I can do that, e.g. in the subscribe part of the code, I want to filter it when creating observable. So, I used flatMap, like this.

let source = Rx.Observable
              .of([{name: 'James', age: 25}, {name: 'Angelina', age: 30}])
              .flatMap(x => x)
              .filter(x => x.age > 15)

While this does what I want, the result is not array anymore, but a stream of objects.

Now, my question is, how do I make an array out of this filtered data again? I don't want to pass single objects to the subscribe part, but an array, as was the case in the first example.

So, the result currently is:

[object Object] {
  age: 25,
  name: "James"
}
[object Object] {
  age: 30,
  name: "Angelina"
}

But I want it to be:

[[object Object] {
  age: 25,
  name: "James"
}, [object Object] {
  age: 30,
  name: "Angelina"
}]

Basically, I'm looking for a way to reverse flatMap. I used .concatMap(x => Rx.Observable.of(x)) to no avail.

The code is available here: http://jsbin.com/lujeqowofi/1/edit

Upvotes: 3

Views: 3321

Answers (3)

andrewgazelka
andrewgazelka

Reputation: 1816

To add on to @Serginho's answer, it might be cleaner to use an empty array as the seed (first value)

const toArray = reduce((acc, x) => {
    acc.push(x);
    return acc;
}, Array<any>());

Upvotes: 0

Serginho
Serginho

Reputation: 7490

Reduceoperator is what you are looking for:

let source = Rx.Observable
              .of([{name: 'James', age: 25}, {name: 'Angelina', age: 30}])
              .flatMap(x => x)
              .reduce((acc, x) => (acc instanceof Array) ? acc.push(x): [acc, x]);

Upvotes: 3

snorkpete
snorkpete

Reputation: 14574

I think you're misunderstanding what RxJS is about. Maybe if I walk you through what your code is doing, you'll see how you went wrong.

let source = Rx.Observable
              .of([{name: 'James', age: 25}, {name: 'Angelina', age: 30}])  // 1.
              .flatMap(x => x)  // 2. 
              .filter(x => x.age > 15)  // 3.

The key abstraction of Observable is that it's a "stream of values over time". By that we mean that observables emit single values, one value at a time. Those values being emitted may be complex objects or even arrays, but they are still single values.

In your code, when you said Observable.of() (step 1), you made an observable that can only emit a single value: your array.

Then, when you flatMapped (step 2), you did something i don't think you meant to do. The flatMap operator is meant to work with an Observable that emits Observables as its values. However, in your step 2, your original observable isn't emitting observables, it's emitting an array. But, flatMap is able to convert an array into an observable, so that's what it did.

But at this point, you now have an observable that will emit 2 values: first -> {name: 'James', age:25 } and then --> {name: 'Angelina', age: 30}. At this point, your observable is no longer dealing with arrays. It's dealing with individual objects.

If your aim is just to filter your original array to only include items that have an age > 15, then use a map on your array.

Upvotes: 2

Related Questions