firebaserxjsgoogle-cloud-firestorerxfire

Combine Observables into a single Observable from a dynamic array of Observable?


Summary: essentially I need something like from Users where usergroupIds in [1,3,5].

Given usergroupIds$ emit an array of group IDs [1,3,5]

I want to union all users by userids in usergroupIds and combine the userIds (distinct)

Here is what I came up with:

usergroupIds$.subscribe(usergroupIds => {
    const users$s = usergroupIds.map(gid => createUsersObservable(gid))
    //  [users$ in group 1, users$ in group 3, users$ in group 5]
    const users$ = combineLatest(...user$s).pipe(
        distinct(user => user.id)
    )
})

createUsersObservable = gid => 
  collectionData(db.collection('users').where('groupId', '==', gid)) // rxFire firestore

Unsubscribe & resubscribe users$ every time on change seems wrong?

Is it possible to express users$ fully in RxJS without creating it in a subscription every time?

Update: With help from @FanCheung:

combinedUsers$ = userGroupIds$.pipe(
        switchMap(userGroupIds => {
            const users$s = userGroupIds.map(groupId =>
                createUsersObservable(groupId))
            return combineLatest(...users$s)
        })

However, since usersObservable emit an array of users at a time, The combinedUsers$ results in somthing like [[userA, userB], [userB, userC], [userA, userD]] which I don't mind to carry out the extra processing on subscribe:

combinedUsers$.subscribe(combinedUsers => {
        const userMap = {}
        for (const users of combinedUsers) 
            users.forEach(user => (userMap[user.id] = user))
        const uniqueUsers = Object.values(userMap)

        // update UI to uniqueUsers
    })

However, is there a way to use somehow flatten the results of combinedUsers$, and then carry out the distinct operator?


Solution

  • See if this work. Basically it fires when usergroupIds$ emits and thus you get your new dynamic observable. shareReplay is needed if you always want the source observable usergroupdIds$ to emit the last saved value when subscribed

    usergroupIds$.pipe(
    shareReplay(1),
    switchMap(usergroupIds => {
        const users$Array = usergroupIds.map(gid => createUsersObservable(gid))
        //  [users$ in group 1, users$ in group 3, users$ in group 5]
       return forkJoin(...users$Array).pipe(
            map(arr=>[].concat(...arr)),
            switchMap(arr=>from(arr)),
            distinct(user => user.id)
        )
    })
    )