问题
I have a service which uses @angular/http
to load data from an API.
I want to create a projection of the retrieved data for my Components
using this data.
Therefore I wrote the following code:
getById(id: string) {
return this.http
.get(`https://my-api.io/${id}`)
.map(response => response.json())
.map(contracts =>
contracts.map(contract => # <- Nested map
new Contract(
contract['id'],
contract['description']
)
)
);
}
In the 6th line I have a nested map
-Statement reducing the readability of my code.
Question
Can I do better? Is there an operator in RxJS which I can use instead of creating this kind of nesting?
Thanks in advance!
回答1:
I propose to use flatMap/selectMany to flatten your nested array into a new stream of each single array element. In a next step you can then use RxJS.map()
to do the actual mapping. Finally collect all mapped array elements with RxJS.toArray()
into a new observable that provides a single array event:
const stream = $http('http://jsonplaceholder.typicode.com/posts')
.map(res => res.data)
.flatMap(posts => posts)
.map(post => Object.assign({}, post, { status: false }))
.toArray();
See sample here: http://jsbin.com/vigizihiwu/1/edit?js,console
But I would also consider: is doing such limbo really necessary? If you only have one subscriber at all, map the received array there:
const stream = $http('http://jsonplaceholder.typicode.com/posts')
.map(res => res.data);
stream.subscribe(res => {
const posts = res.map(post => Object.assign({}, post, { status: false }));
console.log(posts);
});
回答2:
Thanks to @Alexander T.. In my opinion he proposed a nice way to get around the nested map
-Statement: http://jsbin.com/hududiq/1/edit?js,console
Now my code looks like the following:
getByAccountId(id: string) {
return this.http
.get(`http://my-api.io/${id}`)
.map(contractsData => contractsData.json())
.concatAll()
.map(contract => Observable.of(new Contract(
contract['id'],
contract['description'])))
.combineAll();
}
回答3:
This seems like tremendously complicated way to do a very simple thing.
If you want to avoid nested map()
at all cost (even though these are two completely different methods) you can use just one map()
and then forEach()
on the array of results:
new Observable.of(testData)
.map(contractsData => {
var objects = [];
JSON.parse(contractsData).forEach(o => objects.push(new Contract(o['id'], o['description'])));
return objects;
})
.subscribe(val => console.log(val));
This will return the same result as your code.
See live demo: http://plnkr.co/edit/NQgRVSCQGgPvTkPPGz7O (I included also your code to prove the results are identical).
I'd personally use two map()
s anyway because it's even more simple:
new Observable.of(testData)
.map(contractsData => JSON.parse(contractsData).map(o => new Contract(o['id'], o['description'])))
.subscribe(val => console.log(val));
Btw, all operators ending with *all
work with higher order Observables (aka Observables emitting other Observables) and I was surprised your code works because you're passing an ordinary array to .concatAll()
instead of an Observable. It turned out to be an undocumented feature of .concatAll()
thanks to its inner subscriber. According to the documentation this shouldn't work.
来源:https://stackoverflow.com/questions/40170157/rxjs-refactor-nested-map-statement