Rxjs 6,GroupBy,一次发射

时间:2019-06-26 22:09:11

标签: rxjs

如果我有以下数据

[
    { person: 'john', age: 12 },
    { person: 'abby', age: 12 },
    { person: 'kyle', age: 14 },
]

我想要以下结果

[
    [
        { person: 'john', age: 12 },
        { person: 'abby', age: 12 }
    ],
    [
        { person: 'kyle', age: 14 }
    ]
]

正确的rxjs表达式是什么?我目前有

const grouped = source.pipe(
    groupBy(person => person.age),
    mergeMap(group => group.pipe(toArray()))
);

但是输出两次,这使我在Angular中的渲染变得混乱,因为我想渲染所有结果。

// First emit
[
    { person: 'john', age: 12 },
    { person: 'abby', age: 12 }
]
// Second emit
[
    { person: 'kyle', age: 14 }
]

编辑:如果可能的话,我也想看看如何变换对象。

示例输出

[
    [
        age: 12,
        people: [
            { person: 'john', age: 12 },
            { person: 'abby', age: 12 }
        ]
    ],
    [
        age: 14,
        people: [
            { person: 'kyle', age: 14 }
        ]
    ]
]

1 个答案:

答案 0 :(得分:3)

您需要了解RxJs函数的功能以及RxJs映射函数的功能。 RxJs函数适用于流,如果要操作流中的单个值,则需要使用map将函数应用于所发出的项。您需要一个可用于数组而非流的groupBy函数。

groupBy(person => person.age)

您的代码中的这一行没有接收到任何人,而是在接收一群人。

您需要

const grouped = source.pipe(
  map(people => groupBy(people, { keys: ['age'] }))
);

此可观察对象将发射一个分组的对象。在哪里可以找到将数组分组的函数?我在这里https://github.com/adriandavidbrand/ngx-ez/blob/master/projects/ngx-ez/src/lib/ez-core/functions/group-by.ts处写了一个https://stackblitz.com/edit/typescript-iwuzkw?file=group-by.ts的演示,但是对象的形状并不是您想要的。

[
    [
        key: { age: 12 },
        items: [
            { person: 'john' },
            { person: 'abby' }
        ]
    ],
    [
        key: { age: 14 },
        items: [
            { person: 'kyle' }
        ]
    ]
]

但是应该以您想要的方式使用。

可以肯定地将其简化,因为您不需要求和,然后按功能即可,但这是一个起点。

该功能是从我的Easy Angular库中导出的,即“ npm install ngx-ez”,然后您可以通过导入它

import { groupBy } from 'ngx-ez';

如果您不使用Angular,则可以忽略角度依赖性,因为该函数不需要Angular。

const { of } = rxjs;
const { map } = rxjs.operators;

const source$ = of([
    { person: 'john', age: 12 },
    { person: 'abby', age: 12 },
    { person: 'kyle', age: 14 },
]);

const sumGroup = (group, sum) => {
  if (!sum || !sum.length || !group) {
    return group;
  }
  return {
    ...group,
    sum: sum.reduce(
      (sumObj, sumProp) => ({
        ...sumObj,
        [sumProp]: group.items.reduce((a, b) => resolveProperty(a, sumProp) + resolveProperty(b, sumProp))
      }),
      {}
    )
  };
};

const groupBy = (array, grouping) => {
  if (!array) {
    return array;
  }
  const keys = grouping.keys;
  const groups = array.reduce((results, item) => {
    const group = results.find(g => keys.every(key => item[key] === g.key[key]));
    const data = Object.getOwnPropertyNames(item).reduce((o, prop) => {
      if (!keys.some(key => key === prop)) {
        o[prop] = item[prop];
      }
      return o;
    }, {});
    if (group) {
      group.items.push(data);
    } else {
      results.push({
        key: keys.reduce((o, key) => {
          o[key] = item[key];
          return o;
        }, {}),
        items: [data]
      });
    }
    return results;
  }, []);
  return grouping.thenby
    ? groups.map(g => ({ ...g, items: groupBy(g.items, grouping.thenby) }))
    : groups.reduce((arr, g) => {
        arr.push(sumGroup(g, grouping.sum));
        return arr;
      }, []);
};

const grouped$ = source$.pipe(map(people => groupBy(people, { keys: ['age'] })));

grouped$.subscribe(grouped => { console.log(grouped); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.min.js"></script>