How to group data in Angular 2?
Asked Answered
D

4

30

How can I group data in Angular 2 with TypeScript. I am aware that this is done using "group by" filter in Angular 1.X, but not getting how to group data in Angular 2. I have this array:

import {Employee} from './employee';
        export var employees: Employee[];
        employees = [
            { id: 1, firstName: "John", lastName: "Sonmez", department: 1, age: 24, address: "24/7, Working hours apartment, Cal. US", contactNumber: "+968546215789" },
            { id: 2, firstName: "Mark", lastName: "Seaman", department: 2, age: 25, address: "32-C, Happy apartments, Block-9C, Cal. US", contactNumber: "+968754216984" },
            { id: 3, firstName: "Jamie", lastName: "King", department: 3, age: 32, address: "54/II, Glorydale apartment, Cal. US", contactNumber: "+967421896326" },

            { id: 5, firstName: "Jacob", lastName: "Ridley", department: 5, age: 24, address: "24/7, Working hours apartment, Cal. US", contactNumber: "+968546215789" },
            { id: 6, firstName: "Peter", lastName: "Parker", department: 3, age: 25, address: "32-C, Happy apartments, Block-9C, Cal. US", contactNumber: "+968754216984" },
            { id: 7, firstName: "Martin", lastName: "Luther", department: 4, age: 32, address: "54/II, Glorydale apartment, Cal. US", contactNumber: "+967421896326" },
            { id: 8, firstName: "Raghav", lastName: "Kumar", department: 1, age: 34, address: "51/C Shivalik, Cal. US", contactNumber: "+967842569842" },

            { id: 9, firstName: "Narayan", lastName: "Sonmez", department: 3, age: 24, address: "24/7, Working hours apartment, Cal. US", contactNumber: "+968546215789" },
            { id: 10, firstName: "Russell", lastName: "Andre", department: 2, age: 25, address: "32-C, Happy apartments, Block-9C, Cal. US", contactNumber: "+968754216984" },
            { id: 11, firstName: "Ramona", lastName: "King", department: 4, age: 32, address: "54/II, Glorydale apartment, Cal. US", contactNumber: "+967421896326" },
            { id: 12, firstName: "Andre", lastName: "Russell", department: 1, age: 34, address: "51/C Shivalik, Cal. US", contactNumber: "+967842569842" },

            { id: 13, firstName: "Nathan", lastName: "Leon", department: 1, age: 24, address: "24/7, Working hours apartment, Cal. US", contactNumber: "+968546215789" },
            { id: 14, firstName: "Brett", lastName: "Lee", department: 5, age: 25, address: "32-C, Happy apartments, Block-9C, Cal. US", contactNumber: "+968754216984" },
            { id: 15, firstName: "Tim", lastName: "Cook", department: 2, age: 32, address: "54/II, Glorydale apartment, Cal. US", contactNumber: "+967421896326" },
            { id: 16, firstName: "Steve", lastName: "Jobs", department: 5, age: 34, address: "51/C Shivalik, Cal. US", contactNumber: "+967842569842" }
        ];

and I am looking to count the employees by department, like

Department 1 has 4 employees

and so on.

Joining the department id with actual department (so that I can get the department name) is another story I need to figure out.

Dorpat answered 16/5, 2016 at 7:14 Comment(0)
D
64

I would create a custom pipe to do that as described below:

@Pipe({name: 'groupBy'})
export class GroupByPipe implements PipeTransform {
  transform(value: Array<any>, field: string): Array<any> {
    const groupedObj = value.reduce((prev, cur)=> {
      (prev[cur[field]] = prev[cur[field]] || []).push(cur);
      return prev;
    }, {});
    return Object.keys(groupedObj).map(key => ({ key, value: groupedObj[key] }));
  }
}

And then on your template you can write:

<div *ngFor="let item of employees | groupBy: 'department'">
  Department {{ item.key }} has {{ item.value.length }} employees
</div>

See also corresponding plunker https://plnkr.co/edit/49fWY1rMbSZtNQ3H

Decillion answered 16/5, 2016 at 9:5 Comment(9)
Thanks for reply! i will surely give it a try.Dorpat
For me the line ` return Object.keys(groupedObj).map(key => return { key, value: groupedObj[key] });` doesn't work. I get compile error GroupByPipe.ts:20:65 ';' expected. Can you help pleaseJeffers
@Decillion Thank you. I made a Little Change on your answer. Now it works.Jeffers
Woking fine. How to get the actual index of array in loop @DecillionGarish
@Garish I don't know which index you want to get. It's grouped array. Check also this #40341161Decillion
I want to get the original index of array even after applying the groupedby filterGarish
@yurzui, Sir do you think we should use group by as a pipe. As it gets called on each change. angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipeUndershorts
@MilanKamilya Sir, i don't use impure pipe in my exampleDecillion
@Decillion Thanks, Sir.Undershorts
S
0

You can use ngx-pipes https://github.com/danrevah/ngx-pipes#groupby

this.arrayObject = [
  {id: 1, elm: 'foo', value: 0}, 
  {id: 2, elm: 'bar', value: 1}, 
  {id: 3, elm: 'foo', value: 2}, 
  {id: 4, elm: 'foo', value: 2}
];

this.arrayNestedObject = [
  {id: 1, prop: { deep: 'foo' }},
  {id: 2, prop: { deep: 'bar' }},
  {id: 3, prop: { deep: 'foo' }},
  {id: 4, prop: { deep: 'bar' }}
];
<p>{{ arrayObject | groupBy: 'elm' }}</p> 
<!-- Output: "{foo: [{id: 1, elm: 'foo', value: 0}, {id: 3, elm: 'foo', value: 2}, {id: 4, elm: 'foo', value: 2}], bar: [{id: 2, elm: 'bar', value: 1}]}" -->
Ssm answered 23/2, 2018 at 15:28 Comment(0)
R
0
var dept = employees.map((m) => m.department).filter((f, i, ar) => ar.indexOf(f) === i);
console.log(dept);

var group = employees.reduce((accumulator, item, i, arr) => {
  if (dept.length) {
    var pop = dept.shift();
    var list = arr.filter((f) => f.department == pop);
    accumulator.push(...list);
  }
  return accumulator;
}, []);

console.log(group);
Reparative answered 7/8, 2018 at 21:51 Comment(0)
E
0

If you need to access nested properties or need to compare objects you can do

@Pipe({ name: 'groupByProperty' })
export class GroupByPropertyPipe implements PipeTransform {
  transform(value: Array<any>, property: string): Array<any> {
    if (!value) {
      return null;
    }
    const group = value.reduce((previous, current) => {
      const parts = property.split('.');
      let currentValue: any;
      parts.forEach(part => {
        currentValue = currentValue ? currentValue[part] : current[part];
      });
      // Stringify objects for comparison
      currentValue = typeof currentValue === 'object' ? JSON.stringify(currentValue) : currentValue;
      if (!previous[currentValue]) {
        previous[currentValue] = [current];
      } else {
        previous[currentValue].push(current);
      }
      return previous;
    }, {});
    return Object.keys(group).map(key => ({ key, value: group[key] }));
  }
}
Endoskeleton answered 25/2, 2020 at 18:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.