Angularjs groupBy + orderBy
Asked Answered
L

6

11

I am using groupBy from angular-filter to group an array of objects by their date property.

<div ng-repeat="(day, dayEvents) in events | groupBy: 'date' )">
 <h3>{{ day | date: mediumDate }}</h3>
</div>

Which produces the following:

Feb 9, 2015 
Feb 10, 2015 
Feb 11, 2015 
Feb 12, 2015 

How can I reverse the order to start from the most recent date? When I print to the console the array is printed with the order I want:

  Object {
     1423699200000: Array[1],
     1423612800000: Array[7],
     1423526400000: Array[11],
     1423440000000: Array[1]
 }

I also wrote a custom filter to reverse the order after the groupBy:

.filter("reverseOrder", function() {
        function sortNumber(a,b) {
            return  parseInt(b) - parseInt(a);
        }
        return function(collection) {
            var keys = Object.keys(collection).sort(sortNumber);
            var reveredCollection= {};
            var length=collection.length;
            angular.forEach(keys, function(key) {
                reveredCollection[key] = collection[key];
            });
           return reveredCollection;
        }
    })

Which I have applied like this:

<div ng-repeat="(day, dayEvents) in events | groupBy: 'date' | reverseOrder )">
     <h3>{{ day | date: mediumDate }}</h3>
</div>
Lattimer answered 12/2, 2015 at 9:26 Comment(0)
M
20

Recently had the same issue. groupBy creates an object, but the orderBy needs an array, so there's a little bit of funkiness involved. I ended up getting the answer straight from their issues page where one of the authors does an excellent job of explaining it complete with code samples that was basically copy/paste for me.

https://github.com/a8m/angular-filter/issues/57#issuecomment-65041792

Matty answered 20/2, 2015 at 8:13 Comment(0)
B
3

It appears the proper method is to use the | toArray: true | orderBy: customMappingFunction, however, I found the performance on this to be terrible. I came up with a solution that keeps performance up and produces the correct result -- (although it does seem a bit odd!)

<div ng-repeat="(key, results) in allResults | groupBy: '-someKey' )">
    <h3>{{ key | ltrim: '-' }}</h3>
    <ul>
        <li ng-repeat="result in results">
            {{ result }}
        </li>
    </ul>
</div>

For whatever reason, adding the - does force the groupBy to sort correctly, but it also adds it to the key, so we can just remove it!

Bailly answered 28/7, 2016 at 19:39 Comment(0)
E
2

We can achieve this by ordering the data in controller & converting groupBy output to Array.

In controller, order the records before sending it to view :

$scope.events = $filter('orderBy')(events, '-date');

Now the template, receives data in sorted order. After grouping, we need to transform it to Array :

<div ng-repeat="(day, dayEvents) in events | groupBy: 'date' | toArray:true )">
     <h3>{{ day | dayEvents[0].date: mediumDate }}</h3>
</div>
Epinasty answered 16/6, 2015 at 11:18 Comment(0)
T
0

Try this:

 var app = angular.module('myApp', ['angular.filter']);
        app.controller('myCtrl', ["$scope", "$filter", function ($scope, $filter) {        

            $scope.groups = [
                { category: 'alpha', id: 2 },
                { category: 'beta', id: 3 },
                { category: 'gamma', id: 0 },
                { category: 'alpha', id: 4 },
                { category: 'beta', id: 5 },
                { category: 'gamma', id: 1 }
            ];
            // retrieves the min 'id' of a collection, used for the group ordering.
            // you can use lodash instead. e.g: _.min(arr, 'id') 
            $scope.min = function (arr) {
                return $filter('min')
                    ($filter('map')(arr, 'id'));
            }
        }]);
   <script src="https://code.jquery.com/jquery-3.6.0.min.js"
            integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
            crossorigin="anonymous"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.17/angular-filter.min.js" integrity="sha512-f2q5tYQJ0pnslHkuVw7tm7GP7E0BF1YLckJjgLU5z4p1vNz78Jv+nPIEKtZerevbt/HwEfYnRrAo9U3u4m0UHw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
 <div  data-ng-app="myApp" data-ng-controller="myCtrl">      
        <ul ng-repeat="group in groups | groupBy:'category'| toArray:true | orderBy:['category','id']">
            <!-- print the group name -->
            <li>{{ group.$key }}</li>
            <!-- iterate over the group members and order each group by id -->
            <li ng-repeat="item in group | orderBy:'id'">
                {{ item }}
            </li>
        </ul>
    </div>
    
Tyr answered 15/12, 2021 at 6:33 Comment(0)
L
-1

An easier way to reverse an array is to use the code from this answer

app.filter('reverseOrder', function() {
  return function(items) {
    return items.slice().reverse();
  };
});

<div ng-repeat="(day, dayEvents) in events | groupBy: 'date' | reverseOrder )">
     <h3>{{ day | date: mediumDate }}</h3>
</div>
Latham answered 12/2, 2015 at 9:43 Comment(1)
Still the same, it looks like the reverting filter does not have any effect on how the items are displayed..Lattimer
M
-2

try using -

<div ng-repeat="(day, dayEvents) in events | groupBy: '-date' ">

P.S- I have not used groupBy till now anywhere!But this minus thing works well with orderBy attribute!

Melisma answered 18/2, 2015 at 9:4 Comment(1)
groupBy and negative sign are not related. They don't have any relationshipEpinasty

© 2022 - 2024 — McMap. All rights reserved.