RxJs groupBy with an array of objects each having key and values array
Asked Answered
C

3

3

So the requirement is like: I got a array of objects (e.g. Array)

[
   { empDept: 'Engineering', empLoc: 'Pune', empName: 'John' },
   { empDept: 'Engineering', empLoc: 'Mumbai', empName: 'Harry' },
   { empDept: 'HR', empLoc: 'Pune', empName: 'Denis' },
   { empDept: 'Finance', empLoc: 'Mumbai', empName: 'Elvis' },
]

I will have a function which will accept the name of the property as a parameter, on which the employee list will be grouped. So if the input parameter value is 'empDept', then I want the result as below:

[
   {
      key: 'Engineering',
      values: [
         { empDept: 'Engineering', empLoc: 'Pune', empName: 'John' },
         { empDept: 'Engineering', empLoc: 'Mumbai', empName: 'Harry' },
     ]
   },
   {
      key: 'HR',
      values: [
         { empDept: 'HR', empLoc: 'Pune', empName: 'Denis' },
     ]
   },
   {
      key: 'Finance',
      values: [
        { empDept: 'Finance', empLoc: 'Mumbai', empName: 'Elvis' },
     ]
   }
]

I'm using the RxJs groupBy operator to achieve this. I have tried the below code, but it doesn't seem to give the desired result:

this.groupedEmployees$ = employees$.pipe(
      groupBy(person => person.empDept),
      mergeMap(group => of({ key: group.key, values: group.pipe(mergeMap(x => toArray())) })),
    );

Appreciate your help.

Crysta answered 6/5, 2020 at 20:40 Comment(2)
Does this answer your question? RXJS groupBy Observable <Object[]>Eradis
Also: Most efficient method to groupby on an array of objectsEradis
W
9

If you really want to stick with RxJS, you can use this piece of code:

import { from } from 'rxjs'; 
import { groupBy, mergeMap, reduce, toArray } from 'rxjs/operators';

const groupedEmployees$ = from([
  { empDept: 'Engineering', empLoc: 'Pune', empName: 'John' },
  { empDept: 'Engineering', empLoc: 'Mumbai', empName: 'Harry' },
  { empDept: 'HR', empLoc: 'Pune', empName: 'Denis' },
  { empDept: 'Finance', empLoc: 'Mumbai', empName: 'Elvis' }
]).pipe(
  groupBy(person => person.empDept),
  mergeMap(group => group
    .pipe(
      reduce((acc, cur) => {
          acc.values.push(cur);
          return acc;
        },
        { key: group.key, values: [] }
      )
    )
  ),
  toArray()
);

groupedEmployees$.subscribe(console.log);
Wieren answered 7/5, 2020 at 7:44 Comment(2)
Thanks, but why does it console.log twice, separately for each key-value pairs. I fixed it by adding a toArray() operator.Crysta
I'm sorry, I didn't notice you wanted an array of these objects. Yes, adding toArray() would convert it to what you wanted. I'll edit my question.Wieren
C
0

Why would you use rxjs in this case? It's purely array transformation. You can use lodash groupBy: https://lodash.com/docs/#groupBy


const t = [
   { empDept: 'Engineering', empLoc: 'Pune', empName: 'John' },
   { empDept: 'Engineering', empLoc: 'Mumbai', empName: 'Harry' },
   { empDept: 'HR', empLoc: 'Pune', empName: 'Denis' },
   { empDept: 'Finance', empLoc: 'Mumbai', empName: 'Elvis' },
];

const result = Object.entries(_.groupBy(t, 'empDept')).map(([key, values]) => ({key, values}));
Cowry answered 6/5, 2020 at 20:52 Comment(6)
Thanks for you answer. But I prefer RxJs because I'm dealing with observables.Crysta
That's what we call overkill: you make it more complex than it should be. Do not forget rxjs purpose: RxJS is a library for composing asynchronous and event-based programs by using observable sequences. It provides one core type, the Observable, satellite types (Observer, Schedulers, Subjects) and operators inspired by Array#extras (map, filter, reduce, every, etc) to allow handling asynchronous events as collections. and Think of RxJS as Lodash for events.Cowry
Why would using RxJS be considered an overkill whilst using Lodash wouldn't?Wieren
You can even do it without Lodash. With lodash, you can write it in 1 line like I did, with only one method groupBy. How many lines did you write with rxjs, and with how many functions?Cowry
Yes, Lodash fixes this problem in one line, but what if OP's employees$ Observable is an asynchronous source that was already handled by RxJS so they just wanted to continue using RxJS not wanting to mix different approaches (e.g. adding Lodash solution)? I would call using two libraries as an overkill. And I'm not sure how would you solve this in one line without Lodash.Wieren
Mixing libs is not a problem if they are modular. In case of lodash, use lodash-es. If you want to transform an Observable, there is map operator.Cowry
A
0

Are you looking for something like this?

function groupBy(key, array) {
  return array.reduce((all, current) => {
    const existingKey = all.find(existing => existing.key === current[key]);
    if (!existingKey) {
      all.push({key: current[key], values: [current]});
    } else {
      existingKey.values.push(current);
    }
    return all;
  }, []);
}

Example:

const arr = [
   { empDept: 'Engineering', empLoc: 'Pune', empName: 'John' },
   { empDept: 'Engineering', empLoc: 'Mumbai', empName: 'Harry' },
   { empDept: 'HR', empLoc: 'Pune', empName: 'Denis' },
   { empDept: 'Finance', empLoc: 'Mumbai', empName: 'Elvis' },
];

const byDept = groupBy('empDept', arr);
const byLoc = groupBy('empLoc', arr);
Aedile answered 6/5, 2020 at 21:4 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.