RxJS:使用 groupBy 并根据值创建 key/value 对

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

假设我有这个 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 },
]);

我想获得以下格式的可观​​察数组,其中结果按动物类型分组,按动物字母顺序排序,颜色值转换为速度值的键:

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

用groupBy可以吗?到目前为止,我发现的大多数示例都非常不同,结果在一个数组中,而不是组合成 key/value 对。

IMO 更适合这种情况的是 RxJS reduce operator + Array#findIndex function to group the objects and RxJS map operator + Array#sort 对数组元素进行排序的函数。

尝试以下方法

const { from } = rxjs;
const { map, reduce } = rxjs.operators;

const 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 }, ]);

animals$.pipe(
  reduce((acc, curr) => {
    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) =>
    animals.sort((a1, a2) =>
      a1.animal > a2.animal ? 1 : a1.animal < a2.animal ? -1 : 0
    )
  )
).subscribe(console.log);
.as-console-wrapper { max-height: 100% !important; top: 0px }
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.4.0/rxjs.umd.min.js"></script>

工作示例:Stackblitz

另一个解决方案如下:

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;
    })
  )

工作示例:Stackblitz