RXJS groupBy Observable <object []>

时间:2018-05-14 13:57:36

标签: angular rxjs observable

我有entries$: Observable<BooksOverviewGroup[]>;

enter image description here

我希望通过groupId对它们进行分组。我试过这样:

groupBy(books => books.map(book => book.groupId)),
 mergeMap(innerObs => innerObs.skip(1).map(books => books.map(book => book.groupTitle)));

然而,它没有用。在这种情况下,我如何按groupId进行分组? (Observable<Object[]>)

4 个答案:

答案 0 :(得分:4)

高阶可观察

如果您想要更高阶的可观察量,您实际上可以使用groupBy rxjs运算符。

const data = [
  {groupId: "foo", value: 1},
  {groupId: "foo", value: 3},
  {groupId: "foo", value: 5},
  {groupId: "bar", value: 42},
  {groupId: "bar", value: 1337},
];

from(data).pipe(
  groupBy(item => item.groupId)
)
  .subscribe(console.log);

这将导致一个observable,每个组发出一个observable,并且在每个内部observable上,将发出相应的项目。

可观察群组

如果您想要一个只发出组的observable,那么这实际上与rxjs或其groupBy运算符无关。相反,它只是一个关于如何在Javascript中对数组进行分组的基本问题。

没有内置的方法,但像lodash这样的库都带有这样的功能。你也可以手工完成:

const data = [
  {groupId: "foo", value: 1},
  {groupId: "foo", value: 3},
  {groupId: "foo", value: 5},
  {groupId: "bar", value: 42},
  {groupId: "bar", value: 1337},
];

const groupBy = (data, keyFn) => data.reduce((agg, item) => {
  const group = keyFn(item);
  agg[group] = [...(agg[group] || []), item];
  return agg;
}, {});

of(data).pipe(
  map(data => groupBy(data, item => item.groupId)
)
  .subscribe(console.log);

这将为整个数据集发出一次,但会发出

{
  "foo":[
    {"groupId":"foo","value":1},
    {"groupId":"foo","value":3},
    {"groupId":"foo","value":5}
  ],
  "bar":[
    {"groupId":"bar","value":42},
    {"groupId":"bar","value":1337}
  ]
}

答案 1 :(得分:1)

您几乎明白了。

尝试在mergeMap()之前使用concatMap()groupBy()

举个例子:

const people = [
  { name: 'Sue', age: 25 },
  { name: 'Joe', age: 30 },
  { name: 'Frank', age: 25 },
  { name: 'Sarah', age: 35 }
];

of(people) // <- this gets Observable<Object[]>
  .pipe(
    mergeMap(res => res), // <- use concatMap() if you care about the order
    groupBy(person => person.age, p => p.name),
    mergeMap(group => zip(of(group.key), group.pipe(toArray())))
  )
  .subscribe(console.log);

输出:

(2) [25, Array(2)]
(2) [30, Array(1)]
(2) [35, Array(1)]

答案 2 :(得分:0)

只需使用groupBy operator,然后将它们全部合并。

$data.pipe(
    groupBy(book => book.groupId),
    mergeMap(group => group.pipe(toArray()))
)
.subscribe(console.log)

答案 3 :(得分:0)

const data = [
    { groupId: "foo", value: 1 },
    { groupId: "foo", value: 3 },
    { groupId: "foo", value: 5 },
    { groupId: "bar", value: 42 },
    { groupId: "bar", value: 1337 },
];

of(data).pipe(
    mergeMap(res => res),
    groupBy(item => item.groupId),
    mergeMap(obs => {
        return obs.pipe(
            toArray(),
            map(items => {
                return { [obs.key]: items }
            })
        )
    }), toArray()
).subscribe(map((groupResponse) => {
    let groups = {};
    groupResponse.forEach((item) => { groups = { ...groups, ...item }; });
    console.log(groups);
});

输出将为

{
    "foo": [{ "groupId": "foo", "value": 3 }, { "groupId": "foo", "value": 5 }],
    "bar": [{ "groupId": "bar", "value": 42 }, { "groupId": "bar", "value": 1337 }]
}