Home > Net >  RxJS: using groupBy and creating key/value pairs from values
RxJS: using groupBy and creating key/value pairs from values

Time:11-20

Let's say I have this array of observables:

animals$ = from([ 
   { animal: 'rat', color: 'grey', speed: 2 },
   { animal: 'rat', color: 'black', speed: 3 },
   { animal: 'dog', color: 'grey', speed: 2 },
   { animal: 'dog', color: 'black', speed: 1 },
   { animal: 'cat', color: 'grey', speed: 5 },
   { animal: 'cat', color: 'black', speed: 1 },
]);

I want to get an observable array of the following format, where the results have been grouped by animal type, sorted alphabetically by animal, and the color values are transformed to keys for speed values:

[ 
   { animal: 'cat', grey: 5, black: 1 },
   { animal: 'dog', grey: 2, black: 1 },
   { animal: 'rat', grey: 1, black: 3 },
]

Is it possible by using groupBy? Most examples I have found so far are quite different, where in the results are in an array rather than combined to make key/value pairs.

CodePudding user response:

IMO better fit for this scenario would be the RxJS reduce operator Array#findIndex to group the objects and RxJS map operator Array#sort function to sort the elements of the array.

Try the following

this.animals$
  .pipe(
    reduce((acc, curr, index) => {
      const idx = acc.findIndex((item) => item.animal === curr.animal);
      if (idx !== -1) {
        acc[idx] = {
          ...acc[idx],
          [curr.color]: curr.speed,
        };
      } else {
        acc = [
          ...acc,
          {
            animal: curr.animal,
            [curr.color]: curr.speed,
          },
        ];
      }
      return acc;
    }, []),
    map((animals: any) =>
      animals.sort((a1: any, a2: any) =>
        a1.animal > a2.animal ? 1 : a1.animal < a2.animal ? -1 : 0
      )
    )
  )
  .subscribe(console.log);

Working example: Stackblitz

CodePudding user response:

Another solution would be the following:

of([
  { animal: 'rat', color: 'grey', speed: 2 },
  { animal: 'rat', color: 'black', speed: 3 },
  { animal: 'dog', color: 'grey', speed: 2 },
  { animal: 'dog', color: 'black', speed: 1 },
  { animal: 'cat', color: 'grey', speed: 5 },
  { animal: 'cat', color: 'black', speed: 1 },
])
  .pipe(
    map((result) => {
      let res: { animal: string; grey: number; black: number }[] = [];

      const animals = new Set(result.map((x) => x.animal));

      animals.forEach((animal) => {
        const grey: number = result
          .filter((x) => x.animal === animal && x.color === 'grey')
          .map((x) => x.speed)
          .reduce((x, y) => x   y);

        const black = result
          .filter((x) => x.animal === animal && x.color === 'black')
          .map((x) => x.speed)
          .reduce((x, y) => x   y);

        res.push({
          animal,
          grey,
          black,
        });
      });

      return res;
    })
  )

Working example: Stackblitz

  • Related