Reputation: 278
I need to combine two Obersvables.
products
---1
---name: 'product 1'
---supplierId: '1'
---2
---name: 'product 2'
---supplierId: '2'
suppliers
---1
---name: 'supplier 1',
---contact: 'contact 1',
---2
---name: 'supplier 2',
---contact: 'contact2'
In the end I wanna receive the following:
[
{
name: 'product 1'
supplierId: '1',
supplier: { name: 'supplier 1', contact: 'contact1'}
},
{
name: 'product ´2'
supplierId: '2',
supplier: { name: 'supplier 2', contact: 'contact2'}
},
]
At the moment I have the following code:
this.af.list('products')
.map(products => {
let items = [];
(<Array<any>>products).forEach(p => {
if (p.supplierId) {
let supplier$ = this.af.object(`suppliers/${p.supplierId}`).take(1);
items.push(Observable.forkJoin(Observable.of(p), supplier$, (p1, supplier) => {
return {
name: p1.name,
supplier: supplier
};
}))
}
})
return items;
})
.do(res => console.log('After Map', res))
.flatMap(res => Observable.combineLatest(Observable.of(res)))
.subscribe(res => console.log('Final Response', res))
Doesn't matter what I try, I either receive nothing or an Array of ForkJoinObservables.
Upvotes: 0
Views: 2101
Reputation: 17859
Use scan operator. Keep products, suppliers and results in accumulator and join when you can. You might need also cleanup products and suppliers lists when you were able to create a result object( I did not do it)
Scan:
Applies an accumulator function over the source Observable, and returns each intermediate result, with an optional seed value.
http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-scan
let o1$ = Rx.Observable.of({
name: 'product 1',
supplierId: '1'
},
{
name: 'product 2',
supplierId: '2'
});
let o2$ = Rx.Observable.of({
name: 'supplier 1',
contact: 'contact 1'
},
{
name: 'supplier 2',
contact: 'contact 2'
});
Rx.Observable.merge(o1$, o2$)
.scan((acc, el) => {
// product
if (el.supplierId) {
// check in supplier queue
let supplier =
acc.suppliers.find(supplier => supplier.name === ('supplier ' + el.supplierId));
if (supplier) {
acc.results.push({
name: el.name,
supplierId: el.supplierId,
supplier: {name: supplier.name, contact: supplier.contact}
});
}
else {
acc.products.push(el);
}
}
// supplier
else {
// check in product queue
let product =
acc.products.find(product => ('supplier ' + product.supplierId) === el.name);
if (product) {
acc.results.push({
name: product.name,
supplierId: product.supplierId,
supplier: {name: el.name, contact: el.contact}
});
}
else {
acc.suppliers.push(el);
}
}
return acc;
},
{suppliers: [], products: [], results: []}
)
.subscribe(({results}) => console.log(results));
Upvotes: 0
Reputation: 23793
There's no need to use flatMap
nor combineLatest
.
I've made a small Plunkr to demonstrate how to do that.
First of all, I didn't wanted to put too much code that has nothing to do with the problem and I've built my example without Angular, and using mocks :
Mock for data coming from the backend :
// raw objects that simulate the backend
const products = [
{
name: 'product 1',
supplierId: '1'
},
{
name: 'product 2',
supplierId: '2'
}
];
const suppliers = [
{
name: 'supplier 1',
contact: 'contact 1'
},
{
name: 'supplier 2',
contact: 'contact2'
}
];
Mock to simulate your Angular service (which returns an Observable) :
// simulate the angular service with HTTP
// use Observable.of and not Observable.from because we get the product list in one reponse
const getProducts = () => {
return Observable.of(products).delay(1000);
};
const getSupplierByName = (name) => {
const supplier = suppliers.find(s => s.name === `supplier ${name}`);
return Observable.of(supplier).delay(1000);
};
And finally, the code to retrieve your data as expected :
// now let's try to get your data as you want, which is :
// get the whole product list
// for each product, get his supplier
// merge the data into one big result
getProducts()
.switchMap(products =>
Observable
.forkJoin(products
.map(product => getSupplierByName(product.supplierId)
.map(supplier => (Object.assign({}, product, { supplier })))))
)
.do(console.log)
.subscribe();
Here's the result we get in our console :
Upvotes: 3