0
votes

I am trying to find the cleanest solution to use filter() operator in order to filter my observables.

Here I am replicating the service call to get a femaleList separately

export class MyComp implements OnInit {

    maleList: IContact[] = [];
    femaleList: IContact[] = [];    

    constructor(private _contactService: ContactService) { }
    ngOnInit() : void {
        this._contactService.getContacts()
         .filter(male => male.gender === 'M')
        subscribe(maleList => this.maleList = maleList);

        this._contactService.getContacts()
         .filter(female => female.gender === 'F')
        subscribe(femaleList => this.femaleList = femaleList);
     } }

Contactlist

 [{
      "id" : 1,
      "name" : "Todd",
      "gender" : "M"
    }, {
      "id" : 2,
      "name" : "Lillian",
      "gender" : "F"
    }]

Is there any option in RxJS operators with single observable to assign to two variables.

How can i filter the Contacts and assign it to maleList and femaleList using RxJS filter() operator.

Thanks in advance

3

3 Answers

2
votes

You don't need a filter:

this._contactService.getContacts()
  .subscribe(person => {
    if(person.gender === 'F'){
      this.femaleList.push(person);
    } else {
      this.maleList.push(person);
    }
1
votes

If you want to use a single Observable and subscribe to it with two different Observers you'll need to use share() or shareReplay() (which is in RxJS 5 now available only with .publishReplay().refCount()) (See https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/publish.md and https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/sharereplay.md).

let data = [{
    "id" : 1,
    "name" : "Todd",
    "gender" : "M"
}, {
    "id" : 2,
    "name" : "Lillian",
    "gender" : "F"
}];

let maleList = [];
let femaleList = [];

let source = Observable.defer(() => {
        console.log('Observable.defer');
        return Observable.from(data);
    })
    .publishReplay()
    .refCount();

source
    .filter(male => male.gender === 'M')
    .toArray()
    .subscribe(list => maleList = list);

source
    .filter(male => male.gender === 'F')
    .toArray()
    .subscribe(list => femaleList = list);

console.log('maleList', maleList);
console.log('femaleList', femaleList);

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

This prints to console:

Observable.defer
maleList [ { id: 1, name: 'Todd', gender: 'M' } ]
femaleList [ { id: 2, name: 'Lillian', gender: 'F' } ]

Both these subscribers share the same connection to source and at the same time the response is "replayed" (if you subscribe after it was first emitted it'll reemitted without subscribing to the source again).

Note that the items from filter() are emitted one at the time. That's why I used toArray() to collect all values and reemit them as a single array. Alternatively I could call eg. maleList.push() with every value.

Btw, there's also partition() operator that you could use instead of filter() to avoid creating two subscriptions.

0
votes

You could use lodash:

.partition(collection, [predicate=.identity]);

https://lodash.com/docs/#partition

This returns 2 arrays, one where the values evaluate to false & the other to true. Just use 'gender' to build the evaluation.