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
假设我有这个 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