C is for combineLatest
Tags: rxjs, javascript, angular, webdev
Published: 2020-08-09 15:27:07

Let's check out the first function in this series: [combineLatest](https://rxjs-dev.firebaseapp.com/api/index/function/combineLatest).

Use this function whenever you are interested in the latest output of two or more Observables.

One can understand the behavior pretty good when looking at the marble diagram from the official documentation. 
![Marble diagram of combineLatest](https://rxjs-dev.firebaseapp.com/assets/images/marble-diagrams/combineLatest.png)
The upper observables emit the values in the marbles and are combined with combineLatest. As you can see it emits tha latest values of all its Observables whenever on of them emits. These values are then returned as array.

_Enough theory! Show us the example!_

Ok. I first used this operator in the following scenario:

We had a relational database with a many-to-many relationship and two different models that had to be consolidated and displayed in the UI written in Angular. Lets use `Course` and `Student` as examples here:
```typescript
export interface Course {
  id: string;
  name: string;
  studentIds: string[];
}
```
```typescript
export interface Student {
  id: string;
  name: string;
}
```

However the model we want to display should look like this:
```typescript
interface CourseWithStudents {
  id: string;
  name: string;
  students: Student[];
}
```
So we need to merge both models into one view model. 

Lets have a look on how to achieve this with the `combineLatest` operator.

We have two Angular services providing the data in form of an Observable, the `CourseService` and the `StudentService`.
I'm also simulating some http delay in the services using `setTimeout()`.
```typescript
export class CourseService {

  private _courses: Course[] = [
    {id: '1', name: 'German', studentIds: ['1', '3', '4']},
    {id: '2', name: 'Math', studentIds: ['2', '3', '5']},
    {id: '3', name: 'Biology', studentIds: ['1', '2']}
  ];
  courses: BehaviorSubject<Course[]> = new BehaviorSubject<Course[]>([]);

  constructor() {
    setTimeout(() => {
      this.courses.next(this._courses);
    }, 1000);
  }
}
```
```typescript
export class StudentService {

  private _students: Student[] = [
    {id: '1', name: 'Johnny Rakete'},
    {id: '2', name: 'Melissa Woods'},
    {id: '3', name: 'Gordon Thorner'},
    {id: '4', name: 'Jamy Dormer'},
    {id: '5', name: 'Will Higgs'},
    {id: '6', name: 'Smantha Claire Restful'},
  ];
  students: BehaviorSubject<Student[]> = new BehaviorSubject<Student[]>([]);

  constructor() {
    setTimeout(() => {
      this.students.next(this._students);
    }, 2000);
  }
}
```

This is the code that combines both Observable results once any of them emits:
```typescript
this.coursesWithStudents = combineLatest([this.courseService.courses, this.studentService.students])
  .pipe(
    tap(latestResults => {
      console.log('combineLatest emitted', latestResults);
    }),
    map(latestResults => {
      const [latestCourses, latestStudents] = latestResults;
      return latestCourses.map(course => ({
        id: course.id,
        name: course.name,
        students: latestStudents.filter(student => course.studentIds.includes(student.id))
      }));
    }));
```
This is basically just the `combineLatest` operator with some mapping logic attached. The whole code returns an Observable which we can consume e.g. with the `async` pipe directly in the template.

Lets see the output of the above code in three tables, one for courses, one for students and one for the combined result.

![resulting tables](https://raw.githubusercontent.com/Nas3nmann/articles/master/rxjs/the%20ABCs%20of%20rxjs/images/462862b670af6a22c067205d1c328f8c.gif)

I've also integrated some console logging which pops up after the service timeouts emit. You can see that our merged Observable emits three times.
1. When the initial empty arrays are emitted by the BehaviorSubjects 
2. When the courses array is emitted
3. When the students array is emitted

![console output](https://raw.githubusercontent.com/Nas3nmann/articles/master/rxjs/the%20ABCs%20of%20rxjs/images/2094c4e1033e28194b5b94bb07bb38ed.png)

So we achieved what was desired. Every time the courses or students are updated the resulting table gets a fresh merged view model.

Thanks for reading! Feel free to check out the other articles of this series and stay tuned for the next ones.