RXJS 6:递归滤波阵列..与异步滤波器

时间:2019-02-01 17:11:45

标签: angular asynchronous filter rxjs

我需要过滤的对象的递归阵列。 每个对象代表一个webapp路由/ URL。此网址可被限制到了一定的作用。(许可= TRUE | FALSE)或没有,每一个URL可以有子URL ...递归

修改复杂的部分是,过滤需要一个异步函数调用(我在我的项目,该项目的具体需要)。 这就是为什么我尝试使用RXJS做到这一点,但是我可以使用标准数组函数+ async / await来实现它。

我也借此机会多学习一些rxjs,这就是为什么我想要一个面向rxjs回答(这与它涉及异步,这是一个很好的方法胡?)。谢谢

具有此数组:

[
      {
        id: 'level 1.1',
        permission: true,
        children: [
          {
            id: 'level 2.1',
            permission: false,
            children: [
              {id: 'level 3.1'}
            ]
          },
          {
            id: 'level 2.2',
            permission: true,
            children: [
              {id: 'level 3.2'}
            ]
          }
        ]
      },
      {
        id: 'level 1.2'
      },
      {
        id: 'level 1.3',
        permission: false
      }
    ]

我需要过滤它有像(仅保留条目,其中权限不存在或truthy输出:

[
      {
        id: 'level 1.1',
        permission: true,
        children: [
          {
            id: 'level 2.2',
            permission: true,
            children: [
              {id: 'level 3.2'}
            ]
          }
        ]
      },
      {
        id: 'level 1.2'
      }
    ]

我尝试的方法没有递归(注释的代码),因此成功过滤了第一级,但是我不知道如何添加递归:

// simplified ASYNC filter function
promiseMe(x) {
    return Promise.resolve().then(() => {
      return x.permission === undefined || x.permission === true
    });
}

// recursive function
const recursive = arr => {
    return from(arr).pipe(
        mergeMap(entry => from(this.promiseMe(entry)).pipe(
            tap(y => console.log(y)),
            filter(Boolean),
            mapTo(entry),
            tap(console.log),
            mergeMap(item => {
                // here I'm lost
                // I need to affect the result of my async recursive function to item.children : 
              /*return recursive(item.children).pipe(
                  tap(res => {
                    console.log('RES', item, res)
                    item.children = res;
                  })
                );*/

                return of(item);
            })
        )),
        toArray()
    )
};

// main call
recursive(arr).subscribe(x => console.log('finally', x, JSON.stringify(x)))

FIDDLE这里:https://stackblitz.com/edit/angular6-rxjs6-playground-idysbh?file=app/hello.component.ts

1 个答案:

答案 0 :(得分:5)

我无法为什么你需要RxJS处理您的列表搞清楚。

我建议此实现:

const source = [
    {
      id: 'level 1.1',
      permission: true,
      children: [
        {
          id: 'level 2.1',
          permission: false,
          children: [
            {id: 'level 3.1'}
          ]
        },
        {
          id: 'level 2.2',
          permission: true,
          children: [
            {id: 'level 3.2'}
          ]
        }
      ]
    },
    {
      id: 'level 1.2'
    },
    {
      id: 'level 1.3',
      permission: false
    }
];

const isAllow = item => {
  return item.permission === undefined || item.permission;
};

const filtering = (list) => {
  const listing = [];
  list.forEach(item => {
    // If current one have permission.
    if(isAllow(item)) {
      // If he have child, let process it recursively.
      if(item.children && item.children.length > 0) {
        item.children = filtering(item.children);
      }
      // Add current on to whitelisted.
      listing.push(item);
    }
  });
  return listing;
};

console.log(filtering(source));

如果你想打开这个名单上rxjs流,你可以简单地使用map

of(source).pipe(map(source => filtering(source))).subscribe(console.log)

编辑一个:

基于澄清,我以可观察的方式完成了与上面相同的代码。

目标是具有可观察工厂函数(此处为allowOnly$)其中:

  • 创建流,在其中广播当前数组的每个项目。
  • concatMap带有ajax请求的项目。
  • filter项是不允许的。
  • concatMap又是新的combineLatest,是当前项目和allowOnly$的递归调用的组合,其中所有子项都作为参数。
  • toArray将我们当前的项目流转换回单个广播,所有项目合并在阵列上。

Voilà

const dummyAjaxRequest = (item) => {
  return of({
      ...item,
      permission: (item.permission === undefined || item.permission)?true:false
      });
}

const allowOnly$ = items => {
  return from(items).pipe(concatMap(item => {
    return from(
      /**
       * Perform your ajax request here to find what's is allow or not.
       */
      dummyAjaxRequest(item)
    ).pipe(
      /**
       * Exclude what is not allowed;
       */
      filter(item => item.permission),
      concatMap(item => {
        /**
         * If we have child, perform recursive.
         */
        if (item.children) {
          /**
           * combine child and parent.
           */
          return combineLatest(
            allowOnly$(item.children), // Recursive call.
            of(item)
          ).pipe(map(i => {
            return {
              ...i[1], // all property of current,
              children : [...i[0]] // Create new array base on allowed childrens.
            };
          }))
        }
        else {
          /**
           * No child, return simple observable of current item.
           */
          return of(item);
        }
      })
    );
  }), toArray()); // transform stream like --|-|-|-> to --[|,|,|]->
};

of(source).pipe(concatMap(items => {
  return allowOnly$(items);
})).subscribe(console.log);

重要说明所有mergeMap都切换到concatMap来遵守原始列表顺序,而不是混合所有首先基于ajax请求的项目。