Angular 如何使用rxjs expand强制执行深度优先检索?

Angular 如何使用rxjs expand强制执行深度优先检索?,angular,typescript,rxjs,Angular,Typescript,Rxjs,我正在从事Angular 7/Typescript/RxJS 6.3.3项目,使用各种RxJS可观察对象和相关操作符处理通过http服务器从数据库检索到的对象的分层集合(特别是树状集合) 我想使用expand操作符来创建深度优先搜索,并使用concatMap来维持秩序。。。至少我希望如此。不起作用 请参见此处的示例: 该示例的控制台输出为: dfs no delay: [1,8,9,22,23,24,2,4,7,3] dfs with delay: [1,2,3,4,7,8,9,22,23,24

我正在从事Angular 7/Typescript/RxJS 6.3.3项目,使用各种RxJS可观察对象和相关操作符处理通过http服务器从数据库检索到的对象的分层集合(特别是树状集合)

我想使用
expand
操作符来创建深度优先搜索,并使用
concatMap
来维持秩序。。。至少我希望如此。不起作用

请参见此处的示例:

该示例的控制台输出为:

dfs no delay: [1,8,9,22,23,24,2,4,7,3]
dfs with delay: [1,2,3,4,7,8,9,22,23,24]
(第二个输出行可能因添加的延迟量而异。该延迟旨在模拟从http服务器获取数据。)

考虑到示例中的数据,我希望始终获得第一行输出:深度优先排序。示例中的关键函数:

const dfs = (getter: Getter) => rootIds.pipe(
  concatMap(ids => from(ids)),
  expand(id =>
    getter(id).pipe(
      concatMap(children => from(children))
    )
  ),
  toArray()
);

有没有办法强制执行深度优先处理?
expand
是否不能保证这一点,或者这仅仅是一种糟糕的方法,无法将分层数据放入一个平坦的深度优先数组中?

因此我进行了一些尝试,进行了一些手动递归(与依赖
expand
相反),并提出了以下内容(也在上面的stackblitz链接中进行了更新):

(还要注意,我更改了原始post中的树值,以便正确的DFS是从1到21的数字数组)

因此,
workingDFS
可以工作,但比
brokenDFS
慢得多,因为对http服务器的每个请求都必须等待完成,而
brokenDFS
版本将同时运行多个请求(但顺序不正确)


我不知道这里是否有更好的rxjs选项。我可能需要修改我的方法,以便不仅传递感兴趣的对象,而且传递一些结构化/排序信息,同时发出所有/多个请求,然后按正确的顺序组合所有内容。

我认为这是一个好问题,我同意,对于并行获取,您需要一些额外的数据结构来在获取后合成结果

然而,通过
expand
实现递归重建是很有趣的,因此下面是我的顺序尝试:

sequentialDFS(getChildren:Getter,id:number[]):可观察{
返回(ID)。管道(
展开(([head,…rest])=>
//这里我们有一个ID序列
//我们将按从左到右的顺序进行探索,
//例如[1,17,20]将。。。
getChildren(头部)。管道(
开关映射(子孩子=>{
//…将在这里变成[2,6,17,20]
常量acc=[…子项,…剩余];
根据长度返回
?of(acc)
:空的;
})
)
),
//收集人头{{{
地图(([head])=>head),
toArray()
// }}}
);
}
*我稍微修改了getChildren方法,以返回
Observable
,而不是
Observable


这决不是并行抓取的答案。只是分享它,因为它是有趣的。

你试过了WootuStPad和FokJoin的混合吗?@ WANDRILL,我试着考虑如何使用这样的东西来完成我想要的东西,但我的记忆是,<代码>排气图将完全忽略其他值,直到内部完成。这意味着我可能会失去价值。我不知道该怎么做,很酷!甚至没有考虑破坏,但效果很好。而且
getChildren
的签名更改很好。。。我就是这样开始的,看起来更合适/更自然,但我一直在努力工作。@MatthewMoss,我希望这能激励你编写一个并行实现:)GL!事实上,我正在重新辩论,是像我在这里尝试的那样尝试将整个事情扁平化,还是回到列表/项目组件的层次结构,在那里并行性和惰性(即“性能”)几乎是自然的。我有理由尝试走这条路,但我可能高估了其他困难,低估了这些问题。@MatthewMoss,唉,我不知道这段代码的实际应用。。。如果你愿意继续使用原始的rxjs方法,我可能建议你检查一下这个。它是一个数据库驱动的调查生成器(客户端的内部工具)。一个特定的调查将有许多顶级问题,但问题可以嵌套(有条件地,基于父问题的答案)。嵌套可以任意深入,但大多数问题可能只有0-1深,少数2深,很少有3-4深。手头的查询是提供所有问题的列表,以便在添加新问题时选择一个要嵌套的问题。顺便说一句,那个操场太棒了。。。很高兴手头有更多的资源!
class Test {
  // This doesn't maintain depth-first order; it can vary depending on timing.
  brokenDFS(getChildren: Getter, ids: number[]): Observable<number[]> {
    return of(ids).pipe(
      concatMap(ids => from(ids)),
      expand(id => getChildren(id)),
      toArray()
    );
  }

  workingDFS(getChildren: Getter, ids: number[]): Observable<number[]> {
    return from(ids).pipe(
      concatMap(id => this.parentAndChildren(getChildren, id)),
      toArray()
    );
  }

  private parentAndChildren(getChildren: Getter, id: number): Observable<number> {
    return of(id).pipe(
      concat(
        getChildren(id).pipe(
          map(child => this.parentAndChildren(getChildren, child)),
          concatAll()
        )
      ),
    );
  }

}

const getter = getChildrenWithDelay;
const rootIds = [1, 17, 20];
const test = new Test();
test.brokenDFS(getter, rootIds).subscribe(data => console.log(`Broken: ${data}`));
test.workingDFS(getter, rootIds).subscribe(data => console.log(`Working: ${data}`));
Broken: 1,17,20,21,2,6,18,7,13,14,3,4,19,15,16,5,8,9,10,11,12
Working: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21