I can't get my head around a thing that I could do with ease using Lodash.
I need to groupBy
and sum
, something like this, only using RxJs:
let arr = [
{n: 'a', q: 1 },
{n: 'a', q: 2},
{n: 'b', q: 4 }
];
let v = _(arr).chain().groupBy('n').map(sumQt).value()
function sumQt(x) {
return { name: x[0].n, qt: _.sum(x, 'q') }
}
// it produces array like: [{ name: "a", qt: 3 }, { name: "b", qt: 4 }]
jsbin here
I can't get my head around a thing that I could do with ease using Lodash.
I need to groupBy
and sum
, something like this, only using RxJs:
let arr = [
{n: 'a', q: 1 },
{n: 'a', q: 2},
{n: 'b', q: 4 }
];
let v = _(arr).chain().groupBy('n').map(sumQt).value()
function sumQt(x) {
return { name: x[0].n, qt: _.sum(x, 'q') }
}
// it produces array like: [{ name: "a", qt: 3 }, { name: "b", qt: 4 }]
jsbin here
Share Improve this question asked Oct 22, 2015 at 5:20 iLemmingiLemming 36.3k61 gold badges198 silver badges316 bronze badges2 Answers
Reset to default 5I can't think of any way to solve it elegantly using just rx right now - is using rx + lodash ok?
jsbin
// setup
let arr = [{n: 'a', q: 1 },
{n: 'a', q: 2},
{n: 'b', q: 3 }];
function sumQt(x) {
return { name: x[0].n, qt: _.sum(x, 'q') }
}
using lodash
let v = _(arr)
.chain()
.groupBy('n')
.map(sumQt)
.value()
console.log('lodash:', v)
using just rx
Rx.Observable.from(arr)
.groupBy(x => x.n)
.flatMap(group => {
return group.reduce((acc, currentValue) => {
acc.n = currentValue.n;
acc.qt = acc.qt + currentValue.q;
return acc;
}, {n: undefined, qt: 0})
})
.subscribe(sum => console.log('rx:', sum));
which would be prettier if you were fine with using q
instead of qt
Rx.Observable.from(arr)
.groupBy(x => x.n)
.flatMap(group => {
return group.reduce((acc, currentValue) => {
acc.q = acc.q + currentValue.q;
return acc;
})
})
.subscribe(sum => console.log('rx:', sum));
using rx & lodash
Rx.Observable.from(arr)
.groupBy(x => x.n)
.flatMap(group => group.toArray())
.map(sumQt)
.subscribe(sum => console.log('rx+lodash:', sum));
The code below seems to be working for your issue.
- The key part is getting an array back from the groupBy observables. Once you have an array in your hand, you can apply whatever aggregation function you want with the library of your choice.
- Note that this will only work with a source which generate a finite sequence of values, as the
toArray
function will wait for the source to be pleted before releasing the array. - Be also careful that if you create observable from arrays, you will be handling most of the time cold observables, so if you subscribe to those several times, you will replay the values ing out of those arrays. This might or might not be the wanted behaviour. Just keep it in mind.
- Note the use of the
flatMap
in the code below.groupBy
emits streams (one stream for each group), usingtoArray
allows you to aggreate the content of each stream (group) in one array. ThetoArray
operator returns an observable whose unique value is the consolidated array. If you would use themap
operator instead offlatMap
, you would emit an observable instead of the values emitted by that observable.
Code here:
var arr = [{n : 'a', q : 1},
{n : 'a', q : 4},
{n : 'b', q : 4}];
var key_group_by = 'n';
var key_sum_by = 'q';
var groupedArr$ = Rx.Observable.from(arr)
.groupBy(function ( x ) {
return x[key_group_by];
})
.flatMap(function ( groupedKeys$ ) {
// groupKeys$ is an observable
return groupedKeys$
.toArray()
.map(sum_by(key_sum_by));
});
function sum_by ( key ) {
return function sum_by_key ( aHashMap ) {
var result = {};
// Here you could also use your underscore library
var acc = 0;
aHashMap.forEach(function ( value ) {acc += value[key];});
aHashMap[0][key] = acc;
return aHashMap[0];
};
}
groupedArr$.subscribe(emits("groups:"));
function emits ( who ) {
return function ( x ) { console.log([who, "emits"].join(" "), x);};
}
jsbin here : http://jsbin./huqafajudi/edit?html,js,console
console output:
"groups: emits"
[object Object] {
n: "a",
q: 5
}
"groups: emits"
[object Object] {
n: "b",
q: 4
}