Reputation: 297
I have 2 observables that are listening for a database call respectively. I need to merge the 2 arrays together. If I have the following arrays
array1 = [{id: 1, content1: "string"}, {id: 2, content2: "string"}, {id: 3, content3: "string"}]
array2 = [{id: 1, contentX: "string"}, {id: 2, contentY: "string"}, {id: 3, contentZ: "string"}]
I want to merge them together so that I get a single observable array like this:
[{id:1, content1:"string", contentX:"string"}, {id:2, content2:"string", contentY:"string"}, {id:3, content3:"string", contentZ:"string"}]
I have some code but I'm really confused on how to proceed, I can't seem to find the right operators or chain them properly, does anyone have a good explanation on how to proceed? This is what I have so far, but literally don't know how to go on.
const observable1 = getDataFromDb1();
const observable2= getDataFromDb2();
observable1 .pipe(
combineLatest(observable2),
flatMap(x => {
//what to do here???
})
).subscribe(
(value)=>{
console.log(value);
}
)
Thanks for your time
Upvotes: 2
Views: 6107
Reputation: 1030
I tested 3 ways of doing this:
const entityList1 = [{id: 1, name: 'Bob'}, {id: 2, name: 'John'}, {id: 3, name: 'Mike'}];
const entityList2 = [{id: 3, age: 22}, {id: 1, age: 25}, {id: 2, age: 20}];
Same version of @Yoshi:
// V0
console.time('V0')
const mergeListsById_v0 = combineLatest(of(entityList1), of(entityList2)).pipe(
map(([t, s]) => t.map(p => Object.assign({}, p, s.find(q => p.id === q.id))))
).subscribe(x => {
console.log('result V0:', x);
console.timeEnd('V0')
});
Version using rxjs reduce operator:
// V1
console.time('V1')
const mergeListsById_v1 = merge(from(entityList1), from(entityList2)).pipe(
reduce((entitiesById, entity) => {
return {
...entitiesById,
[entity.id]: {
...entitiesById[entity.id] || {},
...entity
}
};
}, {}),
map(entitiesById => Object.values(entitiesById)),
).subscribe(x => {
console.log('result V1:', x);
console.timeEnd('V1')
});
Version using the rxjs groupBy operator:
// V2
console.time('V2')
const mergeListsById_v2 = merge(from(entityList1), from(entityList2)).pipe(
groupBy(entity => entity.id),
mergeMap(groupedEntities$ => groupedEntities$.pipe(
reduce((merged, entity) => ({...merged, ...entity}), {}))
),
toArray()
).subscribe(x => {
console.log('result V2:', x);
console.timeEnd('V2')
});
You can play with these version here: https://stackblitz.com/edit/rxjs-6aqjf9?devtoolsheight=60
As you can see, in terms of performance, the V2 in much better than the others.
Upvotes: 1
Reputation: 54649
I'm taking a wild guess here, and assume that both source-observables emit their values in one big chunk. If that's the case, you simply want to map
both emissions to a custom merged one (otherwise please leave a comment). E.g.:
const { of, combineLatest } = rxjs;
const { map } = rxjs.operators;
// simple merge-by-id
const mergeById = ([t, s]) => t.map(p => Object.assign({}, p, s.find(q => p.id === q.id)));
const db1$ = of([
{id: 1, content1: 'string'},
{id: 2, content2: 'string'},
{id: 3, content3: 'string'},
]);
const db2$ = of([
{id: 1, contentX: 'string'},
{id: 2, contentY: 'string'},
{id: 3, contentZ: 'string'},
]);
const all$ = combineLatest(db1$, db2$).pipe(
map(mergeById)
);
all$.subscribe(console.log);
<script src="https://unpkg.com/[email protected]/bundles/rxjs.umd.min.js"></script>
Upvotes: 3