57

I'm trying to group the items in a ng-repeat using a condition.

An example condition is to group all elements with the same hour.

The data:

[
    {name: 'AAA', time: '12:05'},
    {name: 'BBB', time: '12:10'},
    {name: 'CCC', time: '13:20'},
    {name: 'DDD', time: '13:30'},
    {name: 'EEE', time: '13:40'},
    ...
]

The 'time' field is actually a timestamp (1399372207) but with the exact time the example output is easier to understand.

I am listing these items using ng-repeat:

<div ng-repeat="r in data| orderBy:sort:direction">
   <p>{{r.name}}</p>
</div>

also tried with:

<div ng-repeat-start="r in data| orderBy:sort:direction"></div>
    <p>{{r.name}}</p>
<div ng-repeat-end></div>

A valid output is:

<div class="group-class">
    <div><p>AAA</p></div>
    <div><p>BBB</p></div>
</div>
<div class="group-class">
    <div><p>CCC</p></div>
    <div><p>DDD</p></div>
    <div><p>EEE</p></div>
</div>

My last option if there isn't a simple solution for my problem would be to group the data and then assign it to the scope variable used in ng-repeat.

Any thoughts?

Catalin MUNTEANU
  • 5,618
  • 2
  • 35
  • 43
  • we can create our own data set for these – Nitish Kumar May 06 '14 at 11:31
  • Your output is different form normal – Nitish Kumar May 06 '14 at 11:31
  • 1
    An *on-the-fly-groupBy* filter (or controller-method) of any sort is rather hard to implement in angular, because angular will not recognize the grouped array/object to be *unchanged* and thus run into the dreaded *Infinite $digest Loop*. Your best bet is to implement some sort of stable grouping using `$watch`. It's ugly but probably the only way at the moment. – Yoshi May 06 '14 at 11:32
  • Are you dealing with a defined number of groupings or dynamic based on the data? – bingles Jul 27 '15 at 02:30

3 Answers3

150

You can use groupBy filter of angular.filter module.
so you can do something like this:

usage: collection | groupBy:property
use nested property with dot notation: property.nested_property
JS:

$scope.players = [
  {name: 'Gene', team: 'alpha'},
  {name: 'George', team: 'beta'},
  {name: 'Steve', team: 'gamma'},
  {name: 'Paula', team: 'beta'},
  {name: 'Scruath', team: 'gamma'}
];

HTML:

<ul ng-repeat="(key, value) in players | groupBy: 'team'">
  Group name: {{ key }}
  <li ng-repeat="player in value">
    player: {{ player.name }} 
  </li>
</ul>

RESULT:
Group name: alpha
* player: Gene
Group name: beta
* player: George
* player: Paula
Group name: gamma
* player: Steve
* player: Scruath

UPDATE: jsbin

a8m
  • 9,334
  • 4
  • 37
  • 40
  • This works fine but error in console : `Error: [$rootScope:infdig] `, How to solve it? – Hiren Kagrana Oct 09 '14 at 10:33
  • @HirenKagrana, which version you use ? try to use version 0.4.7 from cdnjs, it's old issue that been solved – a8m Oct 09 '14 at 11:03
  • 1
    I found `sum` filter useful together with `groupBy` in standard table with total scenarios. See http://plnkr.co/edit/PlC93j?p=preview for an example built upon @HireKagrana one. – Patrick Refondini Nov 24 '14 at 18:40
  • What if I have an array with three values and I want to print Color next to the Group Name? $scope.players = [ {name: 'Gene', team: 'alpha', color:'green'}, {name: 'George', team: 'beta', color: 'white'}, {name: 'Steve', team: 'gamma', color:'blue'}, {name: 'Paula', team: 'beta', color:'white' }, {name: 'Scruath', team: 'gamma', color:'reded'} ]; – techwestcoastsfosea Jun 26 '16 at 04:32
  • what if I need to show the team rank with the team name? – Vikas Bansal Jul 09 '16 at 09:49
  • Don't know if you've had the opportunity to play with Angular 2/4, yet; however, it looks like they've removed the `groupBy` clause, which is curious. – Thomas Oct 26 '17 at 15:00
19

First make group in Controller:

 $scope.getGroups = function () {
    var groupArray = [];
    angular.forEach($scope.data, function (item, idx) {
        if (groupArray.indexOf(parseInt(item.time)) == -1) {
            groupArray.push(parseInt(item.time));
        }
    });
    return groupArray.sort();
};

Then Make a Filter for it:

 myApp.filter('groupby', function(){
    return function(items,group){       
       return items.filter(function(element, index, array) {
        return parseInt(element.time)==group;
       });        
    }        
 }) ; 

Then Change Template:

 <div ng-repeat='group in getGroups()'>
     <div ng-repeat="r in data | groupby:group" class="group-class">
         <div><p>{{r.name}}</p></div>
     </div>
 </div>

SEE DEMO

Nitish Kumar
  • 4,850
  • 3
  • 20
  • 38
0

Just a simple HTML solution for static groups.

<ul>
  Group name: Football
  <li ng-repeat="player in players" ng-if="player.group == 'football'">
    Player Name: {{ player.name }} 
  </li>
  Group name: Basketball
  <li ng-repeat="player in players" ng-if="player.group == 'basketball'">
    Player Name: {{ player.name }} 
  </li>
</ul>

Output:

Group name: Football
- Player Name: Nikodem
- Player Name: Lambert
Group name: Basketball
- Player Name: John
- Player Name: Izaäk
- Player Name: Dionisia

empax
  • 80
  • 2
  • 8
  • This solution won't work if the `$index` of the repeated item is needed to reference the item in the `players` collection... – chromaloop Aug 23 '18 at 20:19