Custom GroupBy pipe for Angular 4

Victoria Mineva
04-02-2018

Having worked on various AngularJS applications in the past, I was quite surprised by some built-in functionalities that are not a part of Angular 4. One of them was the groupBy filter, which I’ve used in multiple occasions in the ng-repeat of my AngularJs applications.

 

Custom pipes in Angular 4

It turns out that in Angular 4 (and Angular 2) you have to create your own custom GroupBy pipe in order to group objects from an array inside your *ngFor directive. Luckily there are a lot of examples of custom pipes on the internet and creating one for grouping an array of objects wasn’t so difficult.

Here is the custom pipe that I used in various places of my application:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({name: 'groupBy'})
export class GroupByPipe implements PipeTransform {
    transform(collection: Array, property: string): Array {
        // prevents the application from breaking if the array of objects doesn't exist yet
        if(!collection) {
            return null;
        }

        const groupedCollection = collection.reduce((previous, current)=> {
            if(!previous[current[property]]) {
                previous[current[property]] = [current];
            } else {
                previous[current[property]].push(current);
            }

            return previous;
        }, {});

        // this will return an array of objects, each object containing a group of objects
        return Object.keys(groupedCollection).map(key => ({ key, value: groupedCollection[key] }));
    }
}

Usage

To give you an example of how this pipe will work let’s use this example array of objects:

var myArray = [
    {
        name: "Apple",
        color: "Green"
    },
    {
        name: "Banana",
        color: "Yellow"
    },
    {
        name: "Grape",
        color: "Green"
    },
    {
        name: "Melon",
        color: "Yellow"
    },
    {
        name: "Orange",
        color: "Orange"
    }
];

In our *ngFor directive we can now use the custom pipe to group our array of objects by colour:

<ul>
<li *ngFor="let object of myArray | groupBy:'color'"></li>
</ul>

The result of this is that the pipe will return an array of objects that will look the following way:

[
    {
        key: "Green",
        value: [
            {
                name: "Apple",
                color: "Green"
            },
            {
                name: "Grape",
                color: "Green"
            }
        ]
    },
    {
        key: "Yellow",
        value: [
            {
                name: "Banana",
                color: "Yellow"
            },
            {
                name: "Melon",
                color: "Yellow"
            }
        ]
    },
    {
        key: "Orange",
        value: [
            {
                name: "Orange",
                color: "Orange"
            }
    }
];

Now we can easily display the key of our grouped objects {{ object.key }} or use one more *ngFor directive to loop through object.value and display the separate objects.

LEAVE A REPLY

10 Comments

  • Divakar says:

    collection.reduce is not a function error

  • VL says:

    Thank you that was exactly what I needed

  • subrat says:

    it is giving error :
    geeneric type Array required,

  • subrat bhola says:

    it is working properly but i need to do nested group, how can i achieve

    • Victor Hugo Mello says:

      This works for only one nested object, such as Property.SubProp
      In this scenario: item.property.subproperty :

      your call it is like this:*ngFor=”let item of myCollection| groupBy: ‘property.subproperty’ ”
      And the code changes are:

      import { Pipe, PipeTransform } from ‘@angular/core’;

      @Pipe({name: ‘groupBy’})
      export class GroupByPipe implements PipeTransform {
      transform(collection: any, property: string): any {
      // prevents the application from breaking if the array of objects doesn’t exist yet
      if (!collection) {
      return null;
      }

      const groupedCollection = collection.reduce((previous, current) => {
      const currentValue = this.getNavigationPropertyValue(property, current);

      if (!previous[currentValue.key]) {
      previous[currentValue.key] = [currentValue.value];
      } else {
      previous[currentValue.key].push(currentValue.value);
      }

      return previous;
      }, {});

      // this will return an array of objects, each object containing a group of objects
      return Object.keys(groupedCollection).map(key => ({ key, value: groupedCollection[key] }));
      }

      getNavigationPropertyValue(property: string, current: any): any {
      if (property.indexOf(".") <= 0) {
      return property;
      }

      const tempArray = property.split(".");
      property = tempArray[0];
      const subProperty = tempArray[1];

      if (current == null) {
      return property;
      }

      return { value: current, key: current[property][subProperty] };
      }

      }

  • Son says:

    When I use with infinitive scroll, after scroll event, I use ” myArray.push(…eventData) ” . But it not working. How to solved it ? Thanks!

  • Jasson says:

    Thanks ! Working like a charm !

  • Amit says:

    it is giving error :
    geeneric type Array required,

  • Sadashiva says:

    Will this solution work for grouping on multiple columns?

  • Syed says:

    Thank You.

you might also like