我试图在rxjs中递归地分组数据,我把头发拉了一下。似乎有很多关于不同用例的好例子,但我似乎无法重构代码以满足我的要求。
我可以推断出的核心问题是,在我的map运算符中,我有一个条件返回,它是一个不可观察的或一个可观察的。有没有办法可以重构来解释这种差异?
理想情况下,此函数会将原始“flat”数组按任意数量的列分组,这些列作为cols参数传入。
每次满足条件时,它将以{key:col_name,elements:[... col_elements]}格式附加到数组,其中元素将是相同格式的另一个元素数组,或者是元素的直接列表。
仅将列分组一次(因此从不要求map中的observable发出)时,以下函数有效。
//group data n times based on passed string[] of column attributes
group_data(elements: Observable<any>, cols: string[], index=0) : Observable<any> {
let col = cols[index]
return elements.pipe(
//groupby column value
RxOp.groupBy((el:any) => this.get_groupingValue(el, col)),
//place key inside array
RxOp.mergeMap((group) => group.pipe(
RxOp.reduce((acc, cur) => [...acc, cur], ["" + group.key]))
),
// map to key:group
RxOp.map((arr:any) =>
cols.length <= index + 1?
// no more grouping req
{ 'key': arr[0], 'elements': arr.slice(1) } :
//group recursively, returns Observable instead of array:(
{ 'key': arr[0], 'elements':
this.group_data(from(arr.slice(1)), cols, index + 1)
.pipe(
RxOp.tap(t=> console.log(t)), // not logged
RxOp.reduce((acc, val) => [...acc, val], [])
)
}),
RxOp.toArray()
)
}
//simplified data example:
data = [{id: 'idA', type: 'project', parents: null },
{id: 'idB', type: 'project', parents: null },
{id: 'idC', type: 'episode', parents: ['idA'] },
{id: 'idD', type: 'episode', parents: ['idB'] },
{id: 'idE', type: 'scene', parents: ['idA', 'idC'] },
{id: 'idF', type: 'scene', parents: ['idB', 'idD'] }]
// 1 column passed works correctly as below
group_data(elements: from(data), ['project'])
/* outputted data:
[{key: 'idA',
elements: [ {id: 'idC', type: 'episode', parents: ['idA'] },
{id: 'idE', type: 'scene', parents: ['idA', 'idC'] }]},
{key: 'idB',
elements: [ {id: 'idD', type: 'episode', parents: ['idA'] },
{id: 'idF', type: 'scene', parents: ['idA', 'idC'] }]},
{key: null,
elements: [ {id: 'idA', type: 'project', parents: [] }
{id: 'idB', type: 'project', parents: [] }]}
]
*/
// 2 columns not working correctly
group_data(elements: from(data), ['project', 'episode'])
/*[{key: 'idA',
elements: Observable},
{key: 'idB',
elements: Observable},
{key: null,
elements: Observable}
]*/
我需要采取的方法是重构,以便我可以使用mergeMap而不是map - 这意味着我需要两个可观察的条件而不是一个。从那里我只需要重构,以便映射到键,元素是在mergeMap之后完成的。
这仍然是我第一次涉足rxjs,所以我的解释并不是很好,我对这个问题的总结也不是很好。重要的是,至少它现在表现得如预期的那样。
//group data n times based on passed string[] of column attributes
group_data(elements: Observable<any>, cols: string[], index=0) : Observable<any> {
let col = cols[index]
let grouping = elements.pipe(
//groupby column value
RxOp.groupBy((el:any) => this.get_groupingValue(el, col)),
//place key inside array
RxOp.mergeMap((group) => group.pipe(
RxOp.reduce((acc, cur) => [...acc, cur], ["" + group.key]))
)
)
return grouping.pipe(
RxOp.mergeMap((arr) =>
(
cols.length <= (index +1) ?
//no more grouping required
of(arr.slice(1)) :
//group again
this.group_data(from(arr.slice(1)), cols, index + 1))
// reduce result and put the key back in
.pipe(
RxOp.reduce((acc, cur) => [...acc, cur], ["" + arr[0]])
)
),
// map to key:group
RxOp.map(arr => ({
key: arr[0],
elements: arr.slice(1)
})
),
RxOp.toArray()
)