0

I have a student object that looks like this...

{
  Freshmen: [{id: 3}, {id: 5}],
  Sophomores: [{id: 2}, {id: 6}],
  Juniors: [{id: 1}, {id: 8}],
  Seniors: [{id: 9}, {id: 4}, {id: 7}]
}

I need to do a look up for students based on the ids in the arrays. What I need to be returned is an object that looks exactly the same but where instead of the objects containing just the ids, I now have the full object retrieved from the database.

Inside my angular component.ts file, I am attempting to do so in the following way...

  private getStudents(obj) {
    const result = {};
    for (const key of Object.keys(obj)) {
      obj[key].map(item => {
        this.studentsService.geStudentById(item.id).subscribe(res => {
          result[key] = [];
          result[key].push(res);
        });
      });
    }
    return result;
  }

I have a service injected into the component. The service has a method which gets students by the id and I'm calling the service method inside getStudents(). The service returns the respective student objects which I am then pushing into the array.

Later I am calling the function and assigning the results to a variable studentDetails like so...

this.studentDetails = this.getStudents(studentObj);

Everything seems to be working fine except that when I try to do... console.log(this.StudentDetails.Freshmen); for example, I get undefined.

Am I going about this the right way or is there some better way I could handle it, like maybe using arr.reduce() instead of arr.map()? Although I have tried reduce and it only returns one item for some reason. Your help would be greatly appreciated.

2
  • .subscribe occurs asynchronously, but return result is synchronous. See: stackoverflow.com/questions/14220321/… Commented Dec 11, 2018 at 12:55
  • It all depend where you call the getStudents and where you console.log them. If the console.log is outside of the (async) getStudents scope it will return undefined. Is this.studentsService.geStudentById an async call? Commented Dec 11, 2018 at 13:01

2 Answers 2

1

The problem is that your return statement is executed before your async getStudentById() requests have completed.

I would suggest using RxJS and then zipping your requests together and do something with the data once you have all the results from the requests.

With RxJS your function could look like this:

private getStudents(obj) {
  const result = {};

  // Array to store all the observables
  let observables: Observables<void>[] = [];

  // Generate all the observables with the respective parameters
  for (const key of Object.keys(obj)) {
    obj[key].map(item => {
      observables.push(
        this.studentsService.geStudentById(item.id)
          .map(res => {
            result[key] = [];
            result[key].push(res);
        });
      });
    );
  }

  // Zip them together and wait for all of them to emit a value
  of(...observables)
    .pipe(zipAll())
    .subscribe(() => {
      return result;
    });
}

Additionally, it might be better performance wise to fetch all the students at once and then filtering them instead of making multiple requests just for a single student (depends on the number of students thought).

Sign up to request clarification or add additional context in comments.

Comments

1

You are calling a asynchronously before return result. What is happening is that your function is returning an empty object {} before the all requests are completed.

You need something like this (need some refactor):

function getStudents(obj) {
const result = {};
const requests=  [];
for (const key of Object.keys(obj)) {
   obj[key].forEach(item => {
    let currentRequest = this.studentsService.geStudentById(item.id)
    .subscribe(res => {
      result[key] = [];
      result[key].push(res);
    });
    requests.push(currentRequest)
  });
}
return Observable.forkJoin(...requests).subscribe(() => {
    return result;
})

}

Then set the studentDetails prop like this:

this.getStudents(studentObj).subscribe(result => this.studentDetails = result)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.