Search code examples
angularrxjsobservable

Merging 4 observable with conditions


I need to create an Observable that will collect other observables coming from some http requests. The new observable must be a collection of same object type but every item must be unique. Can you help me writing the correct method to achieve this goal?

// The result observable that I need
topicCollection$ = BehaviorSubject<Topic[]> = new BehaviorSubject<Topic[]>(null);

// Boolean observable of authentication
isAuthenticated: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(null);

// Return default topic
get defaultTopics$(): Observable<Topic[]>{
   return this.defaultTopic.asObservable();
}

// Return topics selected by Admin
get TopTopics$(): Observable<Topic[]>{
  return this.topTopic.asObservable();
}

//Return topics selected by User
get userTopics$: Observable<Topic[]>{
   return this.userTopic.asObservable();
}

//Return user settings 
get userSettings$(): Observable<any[]>{
  return this.userSettings.asObservable();
}

So the constraint are:

  1. If user is not logged collection must be in this order: defaultTopic, topTopic
  2. If user is logged collection must be in this order:defaultTopic, topTopic, userTopic
  3. If user is logged I will read the UserSettings$ that filter the defaultTopics if they will be hidden or shown
  4. Topic must be unique (duplicate topics would be only in default and user topic observable)

I try with combineLatest and forkJoin but I don't know how to differenziate the operators with the authentication observer.


Solution

  • You can try something like this. Of course since your question isn't complete, neither is this answer.

    You'll have to implement a few parts yourself. Still, the following should be a decent place to start tinkering.

    get topicCollection$(): Observable<Topic[]> {
    
      return this.isAuthenticated.pipe(
        take(1),
        switchMap(isLogged => {
    
          // The easy part, merge arrays emitted from two separate streams
          const mergeDefaultTop$ = forkJoin([
            this.defaultTopics$.pipe(take(1)),
            this.topTopics$.pipe(take(1))
          ]).pipe(
            map((v: Topic[][]) => v.flat())
          );
    
          // If the user is logged in, merge then filter the merged topics.
          return !isLogged ? mergeDefaultTop$ : mergeDefaultTop$.pipe(
            switchMap(topics => this.userTopics$.pipe(
              map(userTopics => [...topics, ...userTopics])
            )),
            switchMap(topics => this.userSettings$.pipe(
              map(settings => topics
                // You'll want to update this filter. I can't define it for you 
                // as I dont know what a topic is/ how it's labled, etc.
                .filter(topic => topic !in settings)
                // Filter to remove duplicats. You may want to better define
                // equality depending on your use case (again, I can't do
                // that for you with what you've provided)
                .filter((value, index, self) =>
                  self.indexOf(value) === index
                )
              )
            ))
          );
    
        })
      );
    
    }