I have an object of the following structure:
{
parent1: [
{childId: 1},
{childId: 2}
],
parent2: [
{childId: 3},
{childId: 4}
],
parent3: [
{childId: 5},
{childId: 6}
]
}
The following services:
addFamily(data: any): Observable<Family> {
const body = JSON.stringify(data);
return this.httpClient
.post<Family>(this.apiUrl + '/family', body, this.httpOptions)
}
addParent(data: any): Observable<Parent> {
const body = JSON.stringify(data);
return this.httpClient
.post<Parent>(this.apiUrl + '/parent', body, this.httpOptions)
}
addChild(data: any): Observable<Child> {
const body = JSON.stringify(data);
return this.httpClient
.post<Child>(this.apiUrl + '/child', body, this.httpOptions)
}
There are corresponding "family", "parent", and "child" tables in the database and I want to POST to each of these tables accordingly using RxJS higher order mapping operators to build a data response from prior calls. The idea is:
addFamily()
and return a newfamilyId
addParent()
passing in familyId
and returning a new parentId
addChild()
passing in parentId
for each child createdAfter performing the operations on the example object, there will be:
Currently the codebase is using multiple nested subscribes to perform the above tasks, which is why I looked into RxJS and stumbled upon the below code from a similar question.
private getData(): Observable<VmData> {
return this.service.getSomeData().pipe(
switchMap(session => this.service.getUserData(session.userId).pipe(
switchMap(user => this.service.getMetaData(user.id).pipe(
// by nesting, map has access to prior responses
map(userMeta => this.buildVmData(session, user, userMeta))
))
)),
tap(() => this.isLoading = false)
);
}
The above code would work if it was one family -> one parent -> one child, but how can I modify it to add 1 family, loop through each parent, and add each children under that parent? Any help is greatly appreciated. Thank you.
What if you break down the problem into smaller composable blocks? I can see 3 functions:
createFamily
will call addFamily()
, then use createParent
for each parent of that family.createParent
will call addParent()
, then use createChild
for each child of that parent.createChild
will call addChild()
Something like this
function createFamily(family) {
return defer(() => {
const familyId = generateFamilyId();
return addFamily({ familyId });
}).pipe(
switchMap((result) => {
const { familyId } = result;
const parentCreations = Object.entries(family).map(
([parentId, children]) => createParent({ familyId, parentId, children })
);
return merge(...parentCreations);
})
);
}
function createParent(parent) {
return addParent(parent).pipe(
switchMap(() => {
const { familyId, parentId, children } = parent;
const childrenCreations = children.map(({ childId }) =>
createChild({
familyId,
parentId,
childId,
})
);
return merge(...childrenCreations);
})
);
}
function createChild(child) {
return addChild(child);
}
createFamily(family)
in this case will return an Observable<Child>
emitting all children that was created. You can easily transform this to anything you'd need by changing the .pipe(
of each one of the functions (this is why I also left createChild
as a separate function of addChild
, the idea is you can do transformations there if you need)