6
votes

I have a mock file containing a collection of Tasks, each of which has a due date. Currently (below) I have the creation of a simple observable that, when subscribed to, returns the collection of mocked Tasks. What I'm failing to figure out is how I can process the flat array of Tasks to group them by their due date, returning a structure like;

// Current data structure (unstructured)
[{due: "2016-01-01"}, {due: "2016-01-01"}, {due: "2016-01-02"}, ...]

// Desired structure for consumption
{
  "2016-01-01": [{...}, {...}],
  "2016-01-02": [{...}, {...}, {...}],
  "2016-01-03": [{...}]
}

My current observable creation code is as follows;

// Service...
tasks: Observable<Task[]>;

// init() called from the constructor
private init() {
  this.tasks = Observable.create(observer => {
    observer.next(mockTasks);
  });
}

getTasks() {
  return this.tasks;
}

Which is consumed in my component as follows;

// Component...
ngOnInit() {
  this.taskService.getTasks().subscribe(tasks => {
    this.tasks = tasks; // Contains the collection of tasks as expected
  });
}

This works fine - I get my full array of tasks back as expected. I've tried using the groupBy operator to achieve the above but the observable created via Observable.create() does not appear to have that method available. I've been referring to this resource to attempt to achieve this and I notice that Observable.from() is used instead of .create(), however this also does not appear to be an available function in my service.

  1. Is using RxJS operators (such as groupBy) the way to go with formatting the data as above (and how can this be achieved)? Or should this be manually formatted?
  2. Regarding either answer to 1 - where should this take place?

Thanks!

1
It is always as simple as Observable.from(mockTasks) or Observable.of(mockTasks). It depends on what output you need, because observer.next(mockTasks) creates an observable with single array value, not with several object values. There's no desired method available, it should be added manually with import rxjs/add/observable and import rxjs/add/operator, otherwise you will end up with several basic methods that were initially added by Angular core modules.Estus Flask

1 Answers

3
votes

You're mixing several things, I guess. Observables always have the factor time in it, so you're emitting things over time through the help of an observable. If you only want to group things in a static array, observables are the wrong approach.

In your case you have several dats

const tasks = 
  [ {due: "2016-01-01"}
  , {due: "2016-01-01"}
  , {due: "2016-01-02"}
  ];

If you're about to put them in an Observable, that's possible but it would just output the array once and that's it.

const observable = Rx.Observable.of(tasks);
observable.subscribe(x => console.log('All at once', x));
// output: All at once [ {due: "2016-01-01"},  {due: "2016-01-01"}, {due: "2016-01-02"} ];

If you want to distribute them in time - and now we're getting closer to what Observables are for - you can do this also

const observable2 = Rx.Observable.from(tasks);
observable2.subscribe(x => console.log('One after the other', x));
// output: 'One after the other' {due: "2016-01-01"}
//         'One after the other' {due: "2016-01-01"}
//         'One after the other' {due: "2016-01-02"}

Now let's assume we have a data source that once in a while provides a new task and we want to group them over time. This is what it would look like:

const observable3 = Rx.Observable.from(tasks);
observable3
  .scan((acc, obj) => {
    let oldValue = acc[obj.due] || 0;
    acc[obj.due] = ++oldValue;
    return acc;
  }, {})
  .subscribe(x => console.log(x));
// output: { 2016-01-01: 1 }
//         { 2016-01-01: 2 }
//         { 2016-01-01: 2, 2016-01-02: 1 }

So depending on your needs Observables might be the right thing. If data is distributed in time it's perfectly possible to group them as shown above. I've uploaded the code to jsbin to play around with it.