-1

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;
    }

}
Piotr Deja
  • 31
  • 1
  • 6

2 Answers2

0

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

ruth
  • 29,535
  • 4
  • 30
  • 57
  • I have a question -> is using 2 separate pipes as I did in my attempt valid/justifiable or you should never use 2 separate pipes ? – Piotr Deja Jun 21 '23 at 08:26
  • @PiotrDeja: Using 2 pipes is not wrong per-se, but doesn't add anything to the implementation. It's exactly the same as chaining the operators in a single pipe. More info: https://stackoverflow.com/q/56018885/6513921 – ruth Jun 21 '23 at 09:33
0

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)
        };
      })
    )
Random
  • 3,158
  • 1
  • 15
  • 25
  • Notice that even if I appy your suggestion I get error in second pipe, map(({ tasks, goalsIDs }) => goalsIds is underlined in red – Piotr Deja Jun 21 '23 at 09:30
  • indeed, you in the map, I named the param "goals", not "goalsIDs". You can choose any, but they have to match. I updated my answer so you can use `map(({ tasks, goalsIDs })` after my map – Random Jun 21 '23 at 09:34