Reputation: 44326
I have a BehaviourSubject
that resolves into a collection where each item of has a property children
that also is a BehaviourSubject
resolving into a collection of items.
Now I want to subscribe to my subject and flatten the whole thing into an array where I have a row for each item and each child.
So something in the line of:
interface Parent = {
children: BehaviorSubject<Child[]>
}
Now I want to make another behavior subject out of this that resolves into an array where I have an item for each parent followed by its children.
Starting with:
const collection: BehaviorSubject<Parent[]>;
Want to result to:
const behaviourSubject: BehaviorSubject<(Parent|Child)[]>;
behaviourSubject.subscribe((result: (Parent|Child)[]) => {
...desired result...
});
So let's say the first Parent
in the collection has 3 children, and second has 2 children then if I subscribe to the resulting behaviourSubject
the output should look like this.
result: [Parent, Child, Child, Child, Parent, Child, Child];
How do I achieve this.
I tried to figure out how to do this, by reading in the documentation of RxJS and checking examples, but I guess the recent lack of sleep seems to make my brain work like a dry sponge and I thought the best right now would be to call out for some help on StackOverflow.
Note: if the parent collection or the children get a new value, the result should be updated as well...
Upvotes: 0
Views: 376
Reputation: 11345
Something like below show work, but I wonder why you use BehaviorSubject to add up the complexity
import { first, switchMap, toArray, map } from 'rxjs/operators';
import { pipe, BehaviorSubject, from } from 'rxjs';
//...
const parent1={children:new BehaviorSubject([{name:'john'},{name:'may'},{name:'betty'}])}
const parent2={children:new BehaviorSubject([{name:'key'},{name:'tom'}])}
const collection=new BehaviorSubject([parent1,parent2]);
collection.pipe(
first(),
switchMap(parent=>from(parent)),
switchMap(p=>p.children.pipe(first(),map(c=>[p,...c]))),
toArray(),
map(arr=>arr.flat())
).subscribe(console.log)
if you dont mind make use the .value property from BehaviourSubject the code can be reduced to below
collection.value.map(p=>[p,...p.children.value]).flat()
I will edit this question for those wondering about the different steps in this answer I broke it down in pieces.
It can also be found in this StackBlitz to play around with.
Constructing the data:
import { first, switchMap, toArray, map } from 'rxjs/operators';
import { pipe, BehaviorSubject, from } from 'rxjs';
class Child {
constructor(public name: string){
}
}
class Parent {
constructor(public children: BehaviorSubject<Child[]>){
}
}
const collection = new BehaviorSubject([
new Parent(new BehaviorSubject([
new Child('john'),
new Child('may'),
new Child('betty'),
])),
new Parent(new BehaviorSubject([
new Child('key'),
new Child('tom'),
])),
]);
Here the final observable with its different operations in the pipes with comments added to explain what they exactly do when subscribing to the result:
collection.pipe(
//Emit the first value
first(),
// Complete previous inner observable, emit values
switchMap((parents: Parent[]) => {
// Emit array as a sequence of values
return from(parents)
}),
// complete previous inner observable, emit values
switchMap((parent: Parent) => {
return parent.children.pipe(
// Emit the first value
first(),
// Apply projection with each value from source
map(
(children: Child[]) => {
// return desired array for each parent
return [parent, ...children];
}
)
)
}),
//Collects all source emissions and emits them as an array when the source completes
toArray(),
//map(arr=>arr.flat())
map((array: (Parent|Child)[][] ) => {
// Flatten the array
return array.flat();
}),
).subscribe(console.log)
Upvotes: 1