Search code examples
javascriptrxjsreactivex

Applying a property to each element in several arrays, then returning a flat map with one array


I have a collection that looks like this

[
  {
    count: 123,
    description: 'some description',
    articles: [
     {...}
    ]
  },
  {
    count: 234,
    description: 'some description',
    articles: [
      {...}
    ]
  }
]

Each object in the collection has a collection of articles. What I need is to apply the description to each article object in the respective collection in each element of the primary collection. I also want to end up with a flat array containing only articles. Clearly I'm using mergeMap incorrectly, but I'm not sure how to do it.

I have tried this

json$.pipe(
    // Filter out empty arrays
    filter(section => section.count > 0),
    // Apply the descriptions 
    map(section => section.articles.map(a => (Object.assign(a, section.sectionName)))),
    mergeMap(x => x.articles)
).subscribe(x => console.log(x));

But the articles do not have the description property in them, and it's not a flat array of articles. I've tried a few things but I'm unsure how to proceed


Solution

  • You only need to concatMap the outer observable, after adjusting each article.

    const { Observable } = Rx;
    const { map, concatMap, filter } = Rx.operators;
    
    const json$ = Observable.from([
      {
        count: 123,
        description: 'some description 123',
        articles: [
          {id: 1},
          {id: 2},
        ]
      },
      {
        count: 234,
        description: 'some description 234',
        articles: [
          {id: 3},
          {id: 4},
        ]
      }
    ]);
    
    const withDescription$ = json$.pipe(
      filter(({count}) => count > 0),
      concatMap(
        ({articles, description}) => Observable.from(articles).map(a => ({...a, description}))
      )
    );
    
    withDescription$.subscribe(console.log);
    <script src="https://unpkg.com/@reactivex/rxjs@^5/dist/global/Rx.min.js"></script>


    If you don't need any special behavior on the inner observable, you could simplify to:

    const withDescription$ = json$.pipe(
      filter(({count}) => count > 0),
      concatMap(
        ({articles, description}) => articles.map(a => ({...a, description}))
      ),
    );