【问题标题】:RxJS: using groupBy and creating key/value pairs from valuesRxJS:使用 groupBy 并从值创建键/值对
【发布时间】:2021-11-19 12:32:43
【问题描述】:

假设我有这个 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?到目前为止,我发现的大多数示例都大不相同,结果是在数组中,而不是组合成键/值对。

【问题讨论】:

  • stackoverflow.com/questions/52793944/… 这个遮篷是不是你的问题
  • 在输入 cat - grey - speed 中是 2。但在输出中 cat - grey - speed 是 1。这是错字吗?
  • @MichaelD 是的,我的错,对不起!
  • 出于兴趣,是否会从重复调用 API 中检索这些数据?

标签: angular rxjs


【解决方案1】:

IMO 更适合这种情况的是 RxJS reduce 运算符 + Array#findIndex 函数对对象进行分组,RxJS map 运算符 + 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 }
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.4.0/rxjs.umd.min.js"&gt;&lt;/script&gt;

工作示例:Stackblitz

【讨论】:

  • 是的,这解决了它!非常感谢
  • 在reduce之前我唯一需要添加的是mergeMap((res) =&gt; res),
  • @mponny:在您提供的示例中,我实际上并没有看到在 reduce 之前需要 mergeMap((res) =&gt; res)。事实上,Stackblitz 示例在没有它的情况下也能正常工作。但是,如果数组的每个元素都是可观察的,那么您将需要它。
  • 我对 observables 很陌生,没有意识到这个例子和我的实际实现之间的区别(由于客户保密,我无法发布这个)。感谢您的解释!
【解决方案2】:

另一种解决方案如下:

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

【讨论】:

  • of 与问题中的from 创建了一个非常不同的流程。示例中的颜色可能是动态的,因此 greyblack 常量不够
  • 是的,我都知道,我同意上面的答案是最好的解决方案。我只在这里放这个,因为在问题中,它不需要动态,所以我认为我介绍了一个对初学者更友好的解决方案。
猜你喜欢
  • 1970-01-01
  • 2016-01-16
  • 2016-07-25
  • 1970-01-01
  • 2021-03-24
  • 2020-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多