0

I have an observable array of objects:

 let people = of([
    { firstName: 'John', lastName: 'Doe' },
    { firstName: 'Jane', lastName: 'Doe' },
    { firstName: 'John', lastName: 'Smith' },
    { firstName: 'Jane', lastName: 'Smith' },
  ])

and a function that uses two properties of the objects and returns an observable:

  function fullName(firstName: string, lastName: string) {
    return of(`${firstName} ${lastName}`)
  }

How Do I combine the two, so the result is this:

fullNamePeople: Observable<{
    firstName: string;
    lastName: string;
    fullName: string;
}[]>

I want to display the final observable in angular using the async pipe.

I've tries a lot of combinations of operators, but they seem to work differently than I thought. for example:

fullNamePeople = this.people.pipe(
    concatMap((people) => {
      return people.map((person) => {
        return ({...person, fullName: this.fullname(person.firstName, person.lastName)})
      })
    }
  ))

returns

fullNamePeople: Observable<{
    fullName: Observable<string>;
    firstName: string;
    lastName: string;
}>

The description of concatMap is "Map values to inner observable, subscribe and emit in order.", and I thought that is exactly what I want, but Obviously it isn't working like I thought it would.

Using map instead of concatMap does return an array, but still with a nested observable:

fullNamePeople: Observable<{
    fullName: Observable<string>;
    firstName: string;
    lastName: string;
}[]>

I got it working using:

fullNamePeople = this.people.pipe(
    mergeMap(people => people.map(person => this.fullName(person.firstName, person.lastName).pipe(
      map(fullName => ({...person, fullName}))
    ))),
    mergeAll(),
    toArray()
  );

But I can't imagine this is the most efficient way..

Bryan
  • 11
  • 2

1 Answers1

0

I do not think there is any simple way to achieve what you are looking for.

The solution I ended up with is the following

people
  .pipe(
    // first you wait for the first Obaservable to notify the array
    // once the value is notified, you switch to a new Observable with switchMap
    switchMap((array) =>
      // you switch to an Obaservable which is a stream of the values of the array
      // with the rxjs from function
      from(array)
    ),
    // then you turn each value of the array into an Observable using the fullName
    // function and you flatten the values of this stream of Observables using
    // mergeMap
    mergeMap((data) =>
      fullName(data.firstName, data.lastName).pipe(
        map((fullName) => ({ ...data, fullName }))
      )
    ),
    // last you use toArray to turn the stream of values into one single
    // array notified when upstream completes
    toArray()
  )

Here a stackblitz of this snippet.

Picci
  • 16,775
  • 13
  • 70
  • 113