skip to Main Content

How to perform operations on 2 separate data collections in 1 function in RxJS so that it returns observable -> as for now it returns Void ? This function is running inside Service of course, and finally I want to subscribe to it in my component.

I assume it requires refactoring, I’m still learning RxJS 🙂

My attempt (it does not work):
https://pastecode.io/s/m1g7a861

// use interface instead class for TS
export interface Task {
  goal_id: string;
  name: string;
  description: string;
  priority: string;
  taskDate: string;
  id: string; // key collection
}

// Base Goal Model
export interface Goal {
  name: string;
  isMainGoal: boolean;
  details: string;
  category: string;
  lifeArea: string;
  creationDate: string;
  priority: string;
  endDate: Date;
  id: string;
}

class MyService {

    getTasksByCategory(category:string):Observable<any> {
        const daysFromThisWeek = this.getDaysFromThisWeek();
        return forkJoin({
          tasks: this.tasksS.tasksCollection(),
          goals: this.goalsS.goalsCollection(),
        })
        // !!! OPERATIONS ON GOALS !!! 
        .pipe(
          // filter goals with category from parameter
          map(({ tasks, goals }) => {
            return goals.filter((item:any) => item.category === category);
          }),
          // get ID's of found goals in the previous step
          map((goals:any) => {
            const goalsIDs = goals.map((item:any) => item.id);
            return goalsIDs;
          })
        )
        // !!! OPERATIONS ON TASKS !!!
        .pipe(
          // get IDs-matching-tasks
          map(({ tasks, goalsIDs }) => {
            let modArr = [] as any;
            goalsIDs.forEach((goalId:any) => {
              const forModArr = tasks.filter((task:any) => task.goal_id === goalId);
              modArr = modArr.concat(forModArr);
          })
          return modArr;
        }),
        map(tasksArr => {
          // get number of IDs-matching-tasks on each week day
          let finalTasks = [] as any;
          daysFromThisWeek.forEach((day:any) => {
              const forFinalTasks = tasksArr.filter((task:any) => task.taskDate === day);
              finalTasks = finalTasks.concat(forFinalTasks.length);
          })
          return finalTasks;
        })
        )
    }
    
    getDaysFromThisWeek() {
        let daysArr = [];
        for(let i=1; i<=7; i++) {
          daysArr.push(dayjs().startOf('week').add(i, "day").format('YYYY-MM-DD'));
        }
        return daysArr;
    }

}

2

Answers


  1. You’re almost there. You are only returning the goalsIDs from the first pipe, so the input for the second pipe do not get the tasks emissions. Also as mentioned in the comments, having multiple pipes to the same input observable isn’t different from having a single pipe containing all operators.

    Instead of performing the map opearations of this.goalsS observable after the forkJoin, perform it before. Then you could use the goalIds to filter the emission of tasksS observable.

    Also I’m certain the filtering operations on tasks emissions could be done better, but I’ll leave that to you.

    Try the following

    getTasksByCategory(category: string): Observable<any> {
        const goalIds$ = this.goalsS.tasksCollection().pipe(
            map((goals) =>
                goals
                    // filter goals with category from parameter
                    .filter((goal: any) => goal.category === category)
                    // get ID's of found goals in the previous step
                    .map((goal: any) => goal.id)
            )
        );
        
        const tasks$ = this.tasksS.goalsCollection();
        const daysFromThisWeek = this.getDaysFromThisWeek();
        
        return forkJoin({
            goalIds: goalIds$,
            tasks: tasks$,
        }).pipe(
          // get IDs-matching-tasks
            map(({ tasks, goalIds }) => {
                let modArr = [] as any;
                goalsIDs.forEach((goalId:any) => {
                    const forModArr = tasks.filter((task:any) => task.goal_id === goalId);
                    modArr = modArr.concat(forModArr);
                })
                return modArr;
            }),
            map(tasksArr => {
                // get number of IDs-matching-tasks on each week day
                let finalTasks = [] as any;
                daysFromThisWeek.forEach((day:any) => {
                    const forFinalTasks = tasksArr.filter((task:any) => task.taskDate === day);
                    finalTasks = finalTasks.concat(forFinalTasks.length);
                });
                return finalTasks;
            })
        )
    }
    

    Edit: Added brief info about error in OP’s code

    Login or Signup to reply.
  2. Your first map is actually removing the tasks result. Instead, do something like this:

        .pipe(
          // filter goals with category from parameter
          map(({ tasks, goals }) => {
            return {
              tasks: tasks,
              goals: goals.filter((item:any) => item.category === category)
            };
          }),
          // get ID's of found goals in the previous step
          map(({ tasks, goals }) => {
            return {
              tasks: tasks,
              goals: goals.map((item:any) => item.id)
            };
          })
        )
    

    You may even merge those

        .pipe(
          // filter goals with category from parameter
          map(({ tasks, goals }) => {
            return {
              tasks: tasks,
              goalsIDs: goals.filter((item:any) => item.category === category)
                          .map((item:any) => item.id)
            };
          })
        )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search