pgcd pgcd -4 years ago 250
TypeScript Question

Angular/ngrx - Group and aggregate values from state

I am trying to figure out how to get a grouped and aggregated array of values (that I can use with ngFor) starting from a list of objects, and for the life of me I can't make it work.
The data (which is a slice of my state) looks something like this:

[{name: "A", value: 1, desc: 'something irrelevant'},
{name: "A", value: 3, desc: 'also other properties'},
{name: "B", value: 2, desc: 'etc.'},
{name: "B", value: 5, desc: 'etc.'}]


And the result I'm trying to get is something like (note that the type is different):

[{name: "A", value: 4}, {name: "B", value: 7}]


So, basically I want to find the distinct "names" and the sum of "value" for all the objects that have that name, with an output that can be used by ngFor | async.

My almost working solution to get the distinct values, at the moment, is:

this.aggregates:Observable<any[]> = this.store
.select(state => state.valuesList)
.map(valuesList => valuesList.sort((a,b) => {return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0);} }))
.flatMap(valuesList => valuesList)
.map(value => value.name)
.distinct();


I'd be happy to start with this; the problem is that, if I don't add a toArray(), Typescript complains about "Type string is not assignable to type any[]"; if I add toArray() after distinct(), it doesn't complain anymore but a subscribe() yields no result.

What am I doing wrong? Should I move everything to the reducer (but then I don't know if I can change the type of objects returned by different actions in the same reducer)?
Any help is very very much appreciated.

UPDATE:
I'd be even happier to have a working groupBy() implementation, since it should be exactly its use case.

Answer Source

You can use groupBy to do what you want, but you have to use the groupBy operator on an observable derived from the list - as groupBy expects an observable of items.

In the following snippet, slice is equivalent to this.store.select(state => state.valuesList)

const slice = Rx.Observable.of([
  { name: "A", value: 1, desc: "something irrelevant" },
  { name: "A", value: 3, desc: "also other properties" },
  { name: "B", value: 2, desc: "etc." },
  { name: "B", value: 5, desc: "etc." }
]);

const grouped = slice.concatMap(list => Rx.Observable
  .from(list)
  .groupBy(item => item.name)
  .mergeMap(group => group
    .reduce((total, item) => total + item.value, 0)
    .map(total => ({ name: group.key, value: total }))
  )
  .toArray()
);

grouped.subscribe(value => console.log(value));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download