-2

I'm trying to get a array of objects when provided a (non-observable) array of usernames (string[]). I would like to have a method that is able to get each user object via getUser(username) (HTTP GET request from Angular in-memory web API) for each provided username string and ultimately return an observable array of the user objects. And then be able to get the array from the observable array. I've been juggling around different ways of trying to figure this out but have been struggling.

Preferably would like to avoid using promises if possible.

Attempted method:

usernamesToUserObjects(usernames: string[]): User[] {
  var members: User[] = [];

  for (let username of usernames) {
    if (username.trim() === '') {
      continue;
    } else {
      this.userService.getUser(username.trim())
       .pipe(
          map((user: User[] | undefined) => {
            if (user !== undefined) {
              console.log(`(debug) ${user.username} was found!`);
              members.push(user);
            }
          })
        );
    }
  }

  // I understand that the requests aren't finishing before continuing to the next but need help on making that occur
  console.log(`(debug) User objects are : ${members}`);
  return members;
}

UserService methods used: (work fine)

usersUrl = '/api/users';

constructor(private http: HttpClient) {}

// have to grab users this way in order to get them from angular web api (can only get by 'id' not by a 'username')
users = this.http
  .get<IUser[]>(this.usersUrl)
  .pipe(retry(2), catchError(this.handleError('getUsers()', [])));

// check if user is in users
getUser(username: string): Observable<IUser | undefined> {
  return this.users.pipe(
    take(1),
    map((users: IUser[]) => {
      return users.find((user) => user.username === username);
    })
  );
}

How could I return an Observable<User[]> and then get User[] from that observable?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
jmo31
  • 21
  • 4
  • 1
    You can't do that. `usernamesToUserObjects` will have to return `Observable` (or `Promise`) because getting the users is _asynchronous_. – jonrsharpe Sep 20 '22 at 22:11
  • thanks for the reply! How would I go about this? How could I return an Observable and then get User[] from that observable? – jmo31 Sep 20 '22 at 22:12
  • 1
    I'd suggest researching the tools RxJS provides for managing parallel observables, so you can go from e.g. an array of observables to an observable of an array (c.f. `Promise.all`). – jonrsharpe Sep 20 '22 at 22:13

1 Answers1

1

The function can't return an User[] because fetching a User seems to be an asynchronous operation. So you can either return a Promise or an Observable. I would go with the Observable approach.

To "await" all Observables, I would use forkJoin as an equivalent to Promise.all()

usernamesToUserObjects(usernames: string[]): Observable<User[]> {
  return forkJoin(
    usernames
      .filter(username => username.trim() !== '')
      .map(username => this.userService.getUser(username.trim()))
  ).pipe(
    map(users => users.filter(user => user !== undefined))
  )
}

Playground

Tobias S.
  • 21,159
  • 4
  • 27
  • 45