RxJS按字段分组并返回新的可观测值
我有以下接口和RxJS按字段分组并返回新的可观测值,rxjs,rxjs5,rxjs6,rxjs-observables,Rxjs,Rxjs5,Rxjs6,Rxjs Observables,我有以下接口和Observable,我想要实现的是在Observable中按机器symbol属性分组,并返回映射的ObservableObservable 我尝试使用RxJS gropBy操作符,但它似乎一个接一个地返回分组数组 machines: Machine[] = [ { amount: 1, id: 1, symbol: "A", price: 1 }, { amount: 1, id: 2, symbol: "A&qu
Observable
,我想要实现的是在Observable
中按机器symbol
属性分组,并返回映射的ObservableObservable
我尝试使用RxJS gropBy操作符,但它似乎一个接一个地返回分组数组
machines: Machine[] = [
{ amount: 1, id: 1, symbol: "A", price: 1 },
{ amount: 1, id: 2, symbol: "A", price: 2 }
];
of(machines).pipe(
takeUntil(this.unsubscribe),
mergeMap(res => res),
groupBy(m => m.symbol),
mergeMap(group => zip(of(group.key), group.pipe(toArray()))),
map(x => { // here I have probably wrong model [string, Machine[]]
const orderMachines = x[1].map(y => { return <OrderMachine>{price: y.price, amount: y.amount, id: y.id }})
return <Order>{ symbol: x[0], machines: orderMachines } })
);
以下是基于您的方法的可能解决方案,但有一些改动:
const machines = [
{ amount: 1, id: 1, symbol: "A", price: 1 },
{ amount: 1, id: 2, symbol: "A", price: 2 },
{ amount: 1, id: 3, symbol: "B", price: 3 }
];
from(machines) // (1)
.pipe(
// (2)
groupBy((m) => m.symbol),
mergeMap((group) => group.pipe(toArray())),
map((arr) => ({
symbol: arr[0].symbol, // every group has at least one element
machines: arr.map(({ price, amount, id }) => ({
price,
amount,
id
}))
})),
toArray(), // (3)
)
.subscribe(console.log);
(1)我将(机器)的更改为(机器)
的,以便将机器的对象一个接一个地发射到流中。在此更改之前,整个阵列立即发射,因此流被中断
(2)我从管道中删除了takeUntil(this.unsubscribe)
和mergeMap(res=>res)
,因为在您的示例中没有理由使用它们takeUntil
不会有任何效果,因为流是有限且同步的。与mergeMap
一起应用的标识函数(res=>res
)在流中是有意义的,而在您的示例中并非如此。或者你的项目真的需要这些操作符,因为你有无限的可观测数据流
(3)toArray()
是将可观察的转换为可观察的。它等待流结束,并作为一个数组一次发出所有流值
编辑:
op提到,他更需要一个与无限流兼容的解决方案,但因为toArray
仅适用于有限流,所以在这种情况下,上面提供的答案不会发出任何信号
为了解决这个问题,我将避免使用rxjs中的groupBy
。在需要将一个流拆分为多个流组的其他情况下,它可能是一个非常强大的工具,但在您的情况下,您只需要将一个数组分组,并且有更简单的方法来实现这一点
this.store.pipe(
select(fromOrder.getMachines)
map((arr) =>
// (*) group by symbol
arr.reduce((acc, { symbol, price, amount, id }) => {
acc[symbol] = {
symbol,
machines: (acc[symbol] ? acc[symbol].machines : [])
.concat({ price, amount, id })
};
return acc;
}, {})
),
)
.subscribe((result) =>
// (**)
console.log(Object.values(result))
);
(*)您可以使用一个vanilla,它返回一个形状为{[symbol:string]:Order}
的对象
(**)结果
在这里是一个对象,但您可以轻松地将其转换为数组,但要应用对象。值(结果)
@kruschid非常感谢您的回复,它工作正常,但不幸的是,当我想将其用于我的存储(ngrx)时,它不工作,类型正常,但在mergeMap
方法之后停止显示日志:
this.store.pipe(select(fromOrder.getMachines),
mergeMap(res => res), // Machine[]
groupBy((m) => m.symbol),
tap(x => console.log(x)), //this shows object GroupedObservable {_isScalar: false, key: "A", groupSubject: Subject, refCountSubscription: GroupBySubscriber}
mergeMap((group) => group.pipe(toArray())),
tap(x => console.log(x)), // this is not printed in console
map((arr) => <Order>({
symbol: arr[0].symbol,
machines: arr.map(({ price, amount, id }) => ({
price,
amount,
id
}))
})),
toArray())) // (3)
this.store.pipe(选择(fromOrder.getMachines),
mergeMap(res=>res),//Machine[]
groupBy((m)=>m.symbol),
点击(x=>console.log(x)),//这将显示对象GroupedObservable{u isScalar:false,键:“A”,groupSubject:Subject,refCountSubscription:GroupBySubscriber}
合并映射((组)=>group.pipe(toArray()),
点击(x=>console.log(x)),//这不会在控制台中打印
地图((arr)=>({
符号:arr[0]。符号,
机器:arr.map({price,amount,id})=>({
价格,
数量
身份证件
}))
})),
toArray())/(3)
我以前从未使用过ngrx,但我的猜测是:mergeMap(res=>res)
中断流。我必须查找select
的签名,发现在您的特定情况下,返回类型将是Obsevable
,对吗?这意味着您可以删除mergeMap(res=>res)
。只有当select
返回observeable
时,在该点使用mergeMap
才有意义。只有这样,才需要一个带有身份函数的mergeMap
(或者更短的mergeAll
),来平展一个流(Observable->Observable
)。是的,你是对的选择返回Observable
,但是如果我删除mergeMap(res=>res)
然后groupBy
m
参数是Machine[]
不是Machine
您是对的。我不知道mergeMap
也可以用于展平阵列。说得好。您的代码无法按预期工作,因为很可能this.store
是一个无限流,但toArray()
(在mergeMap
内部)会等待一个永远不会到达的complete
信号。尝试在第一个mergeMap
之前添加take(1)
,以便在拾取一个最新状态的样本后发送complete
信号。我恐怕无法使用take(1)
,因为它必须使用无限流,在进行此更改后,它只返回一次数据(在我的情况下是空数组)停止“监听”更改,还有其他选择吗?
this.store.pipe(
select(fromOrder.getMachines)
map((arr) =>
// (*) group by symbol
arr.reduce((acc, { symbol, price, amount, id }) => {
acc[symbol] = {
symbol,
machines: (acc[symbol] ? acc[symbol].machines : [])
.concat({ price, amount, id })
};
return acc;
}, {})
),
)
.subscribe((result) =>
// (**)
console.log(Object.values(result))
);
this.store.pipe(select(fromOrder.getMachines),
mergeMap(res => res), // Machine[]
groupBy((m) => m.symbol),
tap(x => console.log(x)), //this shows object GroupedObservable {_isScalar: false, key: "A", groupSubject: Subject, refCountSubscription: GroupBySubscriber}
mergeMap((group) => group.pipe(toArray())),
tap(x => console.log(x)), // this is not printed in console
map((arr) => <Order>({
symbol: arr[0].symbol,
machines: arr.map(({ price, amount, id }) => ({
price,
amount,
id
}))
})),
toArray())) // (3)