0

I have an array of Objects, we'll say it's off type Objects[].

I need to map each Object to another type of Object, eg, DestinationObject[] however there is a property on DestinationObject[] that I need to look in Firebase for so I'll need to do a subscribe, or a pipe(map()).

Unfortunately if I do a Subscribe, I get returned a Subscription, and if I do a pipe(map()) I get an list of Observables, rather an Observable List of Objects (ie I want Observable<Object[]> and not Observable<Object>[]

Here's the code:

  processUsers(users: Users[], groupUsers: Users[]): Observable<MappedUser[]> {
    return groupUsers.map(groupUser => {
      return this.firebaseService.SubscribableFunction(groupUser.id).pipe(map(array => {
        return {
          name: groupUser.name,
          arrayLength: array.length,
        } as MappedUser;
      }));
    });
  }

So two simple array objects, we do get a MappedUser returned, but because of the pipe(map()) on the SubscribableFunction, that returns an Observable and therefore the map on the first line returns a list of Observable. Which wasn't quite what I wanted.

Do you have any solutions? I've tried map, mergeMap, from() of() but it doesn't seem to work as expected.

Update Here's the subscribable function as requested:

  SubscribableFunction(userId: string): Observable<Equipment[]> {
    const collection = this.afs.collection<Equipment>('equipment', ref => ref.where('userId', '==', userId));
    return collection.snapshotChanges().pipe(map(items => {
      return items.map(item => {
        return {
          id: item.payload.doc.id,
          ...item.payload.doc.data(),
        } as Equipment;
      });
    }));
  }
Simon
  • 1,385
  • 1
  • 11
  • 20

1 Answers1

0

You're almost there. Once you've mapped the source array to an array of observables, you'd end with something like [obs1, obs2, obs3, ...]. So here you could use RxJS forkJoin function to trigger all the observables in parallel and get the results as an array of objects.

Try the following

processUsers(users: Users[], groupUsers: Users[]): Observable<MappedUser[]> {
  return forkJoin(groupUsers.map(groupUser => { // <-- return `forkJoin` here
    return this.firebaseService.SubscribableFunction(groupUser.id).pipe(map(array => {
      return {
        name: groupUser.name,
        arrayLength: array.length,
      } as MappedUser;
    }));
  }));
}

Now you could subscribe to the function

processUsers(...).subscribe(
  res => console.log(res), // would log `[{MappedUser}, {MappedUser}, {MappedUser},... ],
  err => // good practice to handle HTTP errors
);

Note: RxJS forkJoin will only emit when all the input observables complete. If you wish to have a stream of data from the observables, you could look into zip or combineLatest. You could find the differences between them here.

ruth
  • 29,535
  • 4
  • 30
  • 57
  • Thanks Michael. That didn't appear to work. Not sure what's going on, but putting some logging in, I see that each groupUser from the map is processed, then all the observables from the SubscribableFunction get output, but I actually get no output on the processUsers.subscribe() – Simon Oct 15 '20 at 09:03
  • @Simon: Did you try a `console.log` in the error callback? The observable wouldn't emit if even one of the observable errors out. – ruth Oct 15 '20 at 10:42
  • Unfortunately no errors. Tried converting this to a Promises, but just get back a List of Promises. – Simon Oct 15 '20 at 11:01
  • It doesn't make sense. Could you please show the implementation of `this.firebaseService.SubscribableFunction()`? Based on it's name and nature I assume it returns an observable. – ruth Oct 15 '20 at 11:10
  • Some further reading of https://stackoverflow.com/questions/59137483/how-to-wait-for-a-promise-to-resolve-inside-a-map https://stackoverflow.com/questions/40140149/use-async-await-with-array-map potentially highlights the problem. They say "Array iterators like map or forEach don't work with promises because they don't know how to await a result. Use a simple for loop instead:", I know that's about promises, but I remember you can't explicitly return a value from a subscribe as it's asynchronous. Sadly, I think I know what I need to do now. – Simon Oct 15 '20 at 12:38
  • That's why I didn't convert it to promises. Theoretically the solution should work. But I'm not familiar with Firestore and I'm not sure what's going wrong here. – ruth Oct 15 '20 at 14:12