Angular/ngrx - 对来自州的值进行分组和聚合
Angular/ngrx - Group and aggregate values from state
我正在尝试弄清楚如何从对象列表开始获取分组和聚合的值数组(我可以将其与 ngFor 一起使用),但我终究无法让它发挥作用。
数据(这是我状态的一部分)看起来像这样:
[{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.'}]
我想要得到的结果是这样的(注意类型不同):
[{name: "A", value: 4}, {name: "B", value: 7}]
所以,基本上我想为所有具有该名称的对象找到不同的 "names" 和 "value" 的总和,以及 ngFor | 可以使用的输出。异步。
我的几乎可以工作的解决方案获得不同的值,目前是:
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();
我很乐意从这里开始;问题是,如果我不添加 toArray(),Typescript 会抱怨 "Type string is not assignable to type any[]";如果我在 distinct() 之后添加 toArray(),它不再抱怨,但 subscribe() 不会产生任何结果。
我做错了什么?我是否应该将所有内容都移动到 reducer(但我不知道我是否可以更改同一 reducer 中不同操作返回的对象类型)?
非常感谢任何帮助。
更新:
我会更高兴有一个工作的 groupBy() 实现,因为它应该正是它的用例。
像这样:
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/from';
import 'rxjs/add/operator/map';
function main() {
const t = [
{ name: "C", value: 3, desc: 'also other properties' },
{ name: "A", value: 1, desc: 'something irrelevant' },
{ name: "B", value: 2, desc: 'etc.' },
{ name: "A", value: 3, desc: 'also other properties' },
{ name: "B", value: 5, desc: 'etc.' }
];
const store = Observable.from(Array(100).fill(t));
const aggregates: Observable<any[]> = store
.map((valuesList) => valuesList
.map((x) => ({ name: x.name, value: x.value }))
.sort((a, b) => a.name.localeCompare(b.name))
.reduce((pre, cur) => {
const len = pre.length - 1;
if (pre[len] && pre[len].name === cur.name) {
pre[len].value += cur.value;
return pre;
}
pre[len + 1] = cur;
return pre;
}, [])
);
return aggregates;
}
main().subscribe((x) => {
console.dir(x, { depth: null });
});
输出为:
[ { name: 'A', value: 4 },
{ name: 'B', value: 7 },
{ name: 'C', value: 3 } ]
[ { name: 'A', value: 4 },
{ name: 'B', value: 7 },
{ name: 'C', value: 3 } ]
[ { name: 'A', value: 4 },....
您可以使用 groupBy
来做您想做的事,但是您必须对从列表派生的可观察对象使用 groupBy
运算符 - 因为 groupBy
需要可观察的项目.
在以下代码段中,slice
等同于 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@5/bundles/Rx.min.js"></script>
我正在尝试弄清楚如何从对象列表开始获取分组和聚合的值数组(我可以将其与 ngFor 一起使用),但我终究无法让它发挥作用。 数据(这是我状态的一部分)看起来像这样:
[{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.'}]
我想要得到的结果是这样的(注意类型不同):
[{name: "A", value: 4}, {name: "B", value: 7}]
所以,基本上我想为所有具有该名称的对象找到不同的 "names" 和 "value" 的总和,以及 ngFor | 可以使用的输出。异步。
我的几乎可以工作的解决方案获得不同的值,目前是:
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();
我很乐意从这里开始;问题是,如果我不添加 toArray(),Typescript 会抱怨 "Type string is not assignable to type any[]";如果我在 distinct() 之后添加 toArray(),它不再抱怨,但 subscribe() 不会产生任何结果。
我做错了什么?我是否应该将所有内容都移动到 reducer(但我不知道我是否可以更改同一 reducer 中不同操作返回的对象类型)? 非常感谢任何帮助。
更新: 我会更高兴有一个工作的 groupBy() 实现,因为它应该正是它的用例。
像这样:
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/from';
import 'rxjs/add/operator/map';
function main() {
const t = [
{ name: "C", value: 3, desc: 'also other properties' },
{ name: "A", value: 1, desc: 'something irrelevant' },
{ name: "B", value: 2, desc: 'etc.' },
{ name: "A", value: 3, desc: 'also other properties' },
{ name: "B", value: 5, desc: 'etc.' }
];
const store = Observable.from(Array(100).fill(t));
const aggregates: Observable<any[]> = store
.map((valuesList) => valuesList
.map((x) => ({ name: x.name, value: x.value }))
.sort((a, b) => a.name.localeCompare(b.name))
.reduce((pre, cur) => {
const len = pre.length - 1;
if (pre[len] && pre[len].name === cur.name) {
pre[len].value += cur.value;
return pre;
}
pre[len + 1] = cur;
return pre;
}, [])
);
return aggregates;
}
main().subscribe((x) => {
console.dir(x, { depth: null });
});
输出为:
[ { name: 'A', value: 4 },
{ name: 'B', value: 7 },
{ name: 'C', value: 3 } ]
[ { name: 'A', value: 4 },
{ name: 'B', value: 7 },
{ name: 'C', value: 3 } ]
[ { name: 'A', value: 4 },....
您可以使用 groupBy
来做您想做的事,但是您必须对从列表派生的可观察对象使用 groupBy
运算符 - 因为 groupBy
需要可观察的项目.
在以下代码段中,slice
等同于 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@5/bundles/Rx.min.js"></script>