RXJS groupBy Observable <Object[]>

15

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

这里输入图片描述

我想按groupId对它们进行分组。 我尝试了以下方法:

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

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


你是否真的想将它分组为更高级别的可观察对象?还是你实际上是要将其转换为一个组列表的可观察对象? - Ingo Bürk
我想按GroupID对其进行分组,并将它们作为数组返回。现在我从Observable<Object[]>开始(因为[]无法使用groupby)。也许我只需要将此Observable<Object[]>转换为普通数组,然后使用普通的groupby。你知道如何将它转换为普通数组吗? - dunaskdunsay
你没有回答我的问题。你想要一个高阶可观察对象(每个组发出一个可观察对象的可观察对象)还是发出组的单个可观察对象? - Ingo Bürk
很抱歉,我想看到两种变体以了解如何处理数据。如果成本太高,我想看到更高阶可观察量的解决方案。谢谢。 - dunaskdunsay
你找到任何解决方案了吗?我也很困惑为什么 groupBy 返回一个数组,所以我无法分组。 - supereschek
4个回答

17

你差不多懂了。

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

举个例子:

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)]

非常感谢您的回答,我很久以前就在寻找这个答案了。 - Mary
这很有趣 :) 您能详细说明您的答案吗? - XRaycat
这个答案对我没有帮助。实际上它返回了:"TypeError: You provided 'function zipOperatorFunction(source) {" 这才是我在寻找的 - Marinos An

13

高阶 Observable

如果你想要一个高阶的 Observable,你其实可以使用 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);

这将产生一个可观察对象,它会为每个组发出一个内部可观察对象,对于每个内部可观察对象,相应的项将被发出。

分组可观察对象

如果你想要一个简单地发出组的可观察对象,那么这实际上与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
感谢您的帮助和时间。然而,在高阶Observable的情况下:from(data).pipe( groupBy(item => item.groupId) )我之前尝试过这个,但它没有起作用,因为我有一个Observable数组,不像Object那样是纯对象;它是一个Object[]。在这种情况下,item中的groupId是未知的,因为item在这种情况下是一个对象数组[]。但是如果我进一步进行:groupBy(books => books.map(book => book.groupId))它会认为我将数组作为键,并且结果是错误的。我将尝试使用组的Observable。 - dunaskdunsay

2

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

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

它不能与Observable<object[]>一起使用。我得到了一个数组作为键。 - dunaskdunsay
1
Rxjs 6.3.2 + Angular 6.1.8 -> 根据@dunaskdunsay所说,你会得到一个数组返回。在下面的代码示例中,typescript给出了错误:“属性age在类型'{name:string; age:number;} []'上不存在”。const people = [{ name: 'Sue', age: 25 }, { name: 'Joe', age: 30 }, { name: 'Frank', age: 25 }, { name: 'Sarah', age: 35 }]; const source = of(people); const example = source.pipe(groupBy(p => p.age)); example.subscribe(val => console.log(val)); - Mcanic

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 }]
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接