最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - map vs concatMap - Stack Overflow

programmeradmin6浏览0评论

I haven't been able to find the answer to this question, but what's the difference between concat map and map? Specifically, I have an example that made it very confusing for me:

const domainsObservable = this.auth.getAuthDomains()
    .shareReplay()
    .concatMap((authDomains) => authDomains.map((domain) => this.toDomain(domain, connectionsObservable)))
    .filter((authDomain) => this.isValidDomain(authDomain))
    .toArray();

This was calling a method from a service, getAuthDomains() that just returned an array of 3 domains. When I used the above implementation, I instead ended up with an array of 4 arrays of which each have an array of 3 domains. Somehow, it quadrupled the amount of data in the response.

I then switched over to the following implementation:

const domainsObservable = this.auth.getAuthDomains()
    .shareReplay()
    .map(authDomains => authDomains.map(domain => this.toDomain(domain, connectionsObservable)))
    .map(authDomains => authDomains.filter(domain => this.isValidDomain(domain)));  

This gave me exactly what I needed - a single array that was correctly filtered.

So why did switching over to map dramatically change the results? Did the order of my operations matter?

Thanks

I haven't been able to find the answer to this question, but what's the difference between concat map and map? Specifically, I have an example that made it very confusing for me:

const domainsObservable = this.auth.getAuthDomains()
    .shareReplay()
    .concatMap((authDomains) => authDomains.map((domain) => this.toDomain(domain, connectionsObservable)))
    .filter((authDomain) => this.isValidDomain(authDomain))
    .toArray();

This was calling a method from a service, getAuthDomains() that just returned an array of 3 domains. When I used the above implementation, I instead ended up with an array of 4 arrays of which each have an array of 3 domains. Somehow, it quadrupled the amount of data in the response.

I then switched over to the following implementation:

const domainsObservable = this.auth.getAuthDomains()
    .shareReplay()
    .map(authDomains => authDomains.map(domain => this.toDomain(domain, connectionsObservable)))
    .map(authDomains => authDomains.filter(domain => this.isValidDomain(domain)));  

This gave me exactly what I needed - a single array that was correctly filtered.

So why did switching over to map dramatically change the results? Did the order of my operations matter?

Thanks

Share Improve this question asked Jan 27, 2018 at 1:56 User 5842User 5842 3,0297 gold badges36 silver badges59 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 20

Simply said the two operators do the following:

  • map() - projects each value into a different value that is propagated further down the chain:

    .map(v => v * 2)
    

    This is basically the same as Array.map().

  • concatMap() - projects each value into an Observable and subscribes to it. The operator is always subscribed to only one inner Observable so if the source Observable emits values faster they're buffered inside concatMap():

    .concatMap(v => Observable.of(v * 2))
    

    ...or typically in Angular you'd do something like this:

    .concatMap(v => this.http.get(`whatever/${v}`))
    

But here comes the interesting part. In RxJS 5 operators that subscribe to inner Observables (Observables returned from projection function, etc.) in fact expect so called "observable input". This means that you can freely interchange Observables, Promises, Observables-like objects, ... and also JavaScript arrays.

Internally it's implemented using the subscribeToResult function. Note that if you pass it an array it'll iterate it and emit each value separatelly: https://github.com/ReactiveX/rxjs/blob/master/src/internal/util/subscribeToResult.ts#L17

This means that you can use concatMap() to "unpack" arrays into single emissions. For example:

Observable.of(42)
  .concatMap(v => [1, 2, 3])
  .subscribe(console.log)

This will print numbers:

1
2
3

On the other hand if you used just map it would simply pass the array further:

Observable.of(42)
  .map(v => [1, 2, 3])
  .subscribe(console.log)

This will print numbers:

[1,2,3]

So concatMap is sometimes used only to unpack an array coming from the source Observable with concatMap(array => array) and that's exactly what's happening in your code.

Which one is better is up to you. Typically it's easier to perform simple operations like map or filter on the Array object itself instead of unpacking and processing in the Rx chain for performance reasons.

发布评论

评论列表(0)

  1. 暂无评论