Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combine multiple observable arrays into new object array

I have 3 observable arrays like below.

persons = [
   {
      "firstName":"john",
      "lastName":"public",
      "locationID":"1",
      "departmentID":"100"
   },
   {
      "firstName":"sam",
      "lastName":"smith",
      "locationID":"2",
      "departmentID":"101"
   }
]

departments = [{"departmentID": "100",
               "name": "development"
               },
               {"departmentID": "101",
                "name": "sales"
               }]

locations = [{"locationID": "1", "name": "chicago"},
              {"locationID":"2", "name": "ny"}]

I am trying to combine these 3 into below result ,

result = [
   {
      "firstName":"john",
      "lastName":"public",
      "location":"development",
      "department":"sales"
   },
   {
      "firstName":"sam",
      "lastName":"smith",
      "location":"ny",
      "department":"sales"
   }
]

To get the desired result, I have used map function on persons observable to give new object array.

this.store<Person>('persons')
.map(function(person){
     let p = new personDetail()
     p.firstName = person.firstName,
     p.lastName = person.lastName
     return p;
})

PersonDetail object has firstName, lastName, location and department properties. How do I do a lookup into departments observable and get a matching row for departmentID to get the department name ?

I am new to rxjs library, please let me know if there is a better way to attain the desired result.

like image 919
Yousuf Avatar asked Dec 14 '22 01:12

Yousuf


2 Answers

Since you'll very likely want to fetch lists of departments and locations from a remote service (make another HTTP request) I'd do it right away with Observables as well.

Observable.from(persons)
    .mergeMap(person => {
        let department$ = Observable.from(departments)
            .filter(department => department.departmentID == person.departmentID);

        let location$ = Observable.from(locations)
            .filter(location => location.locationID == person.locationID);

        return Observable.forkJoin(department$, location$, (department, location) => {
            return {
                'firstName': person.firstName,
                'lastName': person.lastName,
                'location': location.name,
                'department': department.name,
            };
        });
    })
    .toArray()
    .subscribe(result => console.log(result));

This prints to console:

[ { firstName: 'john',
    lastName: 'public',
    location: 'chicago',
    department: 'development' },
  { firstName: 'sam',
    lastName: 'smith',
    location: 'ny',
    department: 'sales' } ]

There're two Observables department$ and location$ that are filtered with filter() operator to get the only item with matching ID. Then forkJoin() operator waits until both of them are complete. Operator mergeMap() then reemits the value returned from forkJoin(). At the end with toArray() we collect all items into a single array.

Instead of Observable.from(...) you can have whatever service you'll need (eg. http.get(...)).

See live demo: https://jsbin.com/nenekup/4/edit?js,console

Similar questions: Merge subarrays using Observables and Subscribing to a nested Observable

like image 52
martin Avatar answered Dec 17 '22 02:12

martin


i think the RxJS .zip operator may be your friend here.

As far as I understand, .zip emits like this ...

.zip(
   Observable.from[array1].switchMap( // map to http response here ),
   Observable.from[array2].switchMap( // map to http response here ),
   Observable.from[array3].switchMap( // map to http response here )
).map((valueFromArray1, valueFromArray2, valueFromArray3) {
   // Create your object here 
})

Something like that! Hopefully, puts you on the right track.

.zip should emit the first time when all 3 have emitted (and it will emit a second time when all three stream have emitted twice, etc) - In your scenario I expect all three stream emit once only, which makes it a simple case for .zip

like image 28
danday74 Avatar answered Dec 17 '22 01:12

danday74