RXJS Вызовите api во вложенном массиве и объедините результат в массивы. - PullRequest
2 голосов
/ 24 сентября 2019

У меня есть объект с массивом семейств.Каждое из этих семейств содержит массив объектов членов семьи.У каждого из членов семьи есть контракт, которого сейчас нет.Моя задача - извлечь контракт из API остальных и объединить его с объектом-участником.

Object {
 content: [{
  familiId: '1',
  familyMemmbers: [
   { id: '1'
     name: 'Tom'
     ...
    },
    { id: '2'
     name: 'Peter'
     ...
    }
   ]
 },
{
  familiId: '2',
  familyMemmbers: [
   { id: '1'
     name: 'Paul'
     ...
    },
    { id: '2'
     name: 'Joe'
     ...
    }
   ]
 }]
}
this.service.getFamilies().switchMap(familiyPage => from(familiyPage.content))
          .switchMap(family => from(family.familyMembers)).mergeMap(member => this.service.getContract(member.id).map(contract => {
          return {...member, contract}

Мой подход возвращает объект { id: 1, name: 'Tom', contract: ....} на первом излучении и { id: 2, name: 'Peter', contract: ....} на втором излучении, { id: 1, name: 'Paul', contract: ....} на третьем излучении и { id: 2, name: 'Joe', contract: ....} на четвертом излучении.

Что я хотел бы иметь, так это то, что существует только один эмитт, который содержит всю структуру данных.т.е.

Object {
 content: [{
  familiId: '1',
  familyMemmbers: [
   { id: '1',
     name: 'Tom',
     contract: {...},
     ...
    },
    { id: '2',
     name: 'Peter',
     contract: {...},
     ...
    }
   ]
 }, ...]
}

Ответы [ 3 ]

0 голосов
/ 25 сентября 2019

Используйте forkJoin, чтобы сгруппировать массив наблюдаемых элементов в наблюдаемый массив элементов.Затем используйте toArray, чтобы сгруппировать наблюдаемые семейства в наблюдаемый массив семейств.Используйте mergeMap, если вас не заботит порядок семей, или concatMap, если вы хотите сохранить семьи в их первоначальном порядке.

this.service.getFamilies().pipe(
  // use concatMap instead of mergeMap if you want to keep the families in their original order
  mergeMap(family => {
    // array of observables
    const members$ = family.familyMembers.map(member => {
      return getContract(member.id).pipe(map(contract => {...member, contract});
    });

    // use forkJoin to return single observable that returns array of results
    return forkJoin(members$).pipe(map(familyMembers => {...family, familyMembers}));
  }),
  // combine all the families back into an array of families
  toArray());
0 голосов
/ 25 сентября 2019

вы работаете с массивами в массиве, поэтому вам нужно вложенные forkJoins для выполнения этого вместе с несколькими назначениями.

   this.service.getFamilies().pipe(
     switchMap(familiesData => 
       forkJoin(
         familiesData.content.map(family => 
           forkJoin(
             family.members.map(member => 
               this.service.getContract(member.id).pipe(
                 map(contract => Object.assign({}, member, {contract})) // assign contract
               )
             )
           ).pipe(
             map(familyMemmbers => Object.assign({}, family, {familyMemmbers})) // assign members
           )
         )
       ).pipe(
         map(content => Object.assign({}, familiesData, {content})) // assign content
       )
     )
   );

шаг за шагом:

  1. получить семейства

  2. switchMap для forkJoin

  3. содержимое сопоставлено с forkJoin из

  4. члены, сопоставленные с getContract

  5. , назначают результирующие контракты членам и возвращают участников с контрактами

  6. назначают результирующие familyMembers семьям и возвращают семьи с familyMembers

  7. назначить результирующие семейства контенту и вернуть новый контент

0 голосов
/ 24 сентября 2019

Просто добавьте rxjs pipeAble оператор toArray (), это объединит все выданные

. Вы можете увидеть код ниже, где мы транслируем массив объектов, затем потоковые члены семьи и запрашиваем HTTP-вызов для каждого члена семьизатем сопоставьте ответ с членом семейства Object.than снова сопоставляя результирующие члены семейства с объектом семейства и объедините их, используя toArray ().

const appDiv = document.getElementById('app');
appDiv.innerHTML = `<h1>if you dont want concurrent request then you can use concat instead of merge</h1>`;
const { fromEvent, merge, of, from } = rxjs;
const { map, filter, concatMap, toArray, tap, delay, concatAll, mergeMap,mergeAll } = rxjs.operators;
// Write TypeScript code!
console.clear();

const data$ = of([{
  familiId: '1',
  familyMemmbers: [
   { id: '1',
     name: 'Tom'
    },
    { id: '2',
     name: 'Peter'
    }
   ]
 },
{
  familiId: '2',
  familyMemmbers: [
   { id: '1',
     name: 'Paul'
    },
    { id: '2',
     name: 'Joe'
    }
   ]
 }])
const q = (x) => {
  // Fake http call 
  const oneSecondSource$ = of(x['id']).pipe(delay(1 * x.id))
  return oneSecondSource$.pipe(map(abs => {
    
    return { ...x, contarct: x['id'] + x['name'] }
  }));
}

const p = (obj) => {
  // mapping each
  return from(obj.familyMemmbers).pipe(mergeMap(q), toArray(), map(a => {
    return { ...obj, familyMemmbers: a }
  }))
}
const example$ = data$.pipe(mergeMap(a => from(a).pipe(map(p))),
  mergeAll(),
  toArray()
);
example$.subscribe(console.log)
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>

<div id="app"></div>

Для вашего вопроса нажмите здесь для решения Stackblitz

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