RXJS 6: рекурсивная фильтрация массива .. с асинхронным фильтром - PullRequest
0 голосов
/ 01 февраля 2019

Мне нужно отфильтровать рекурсивный массив объектов.Каждый объект представляет маршрут / URL веб-приложения.Этот URL-адрес может быть ограничен определенной ролью (access = 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
      }
    ]

Мне нужно отфильтровать его, чтобы получить такой вывод (оставляйте только записи, где разрешение отсутствует или верно:

[
      {
        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 голосов
/ 01 февраля 2019

Я не могу понять, зачем вам нужен 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...