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
属性分组,并返回映射的Observable
Observable

我尝试使用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)