Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GroupBy + OrderBy not working #57

Closed
jtomek opened this issue Nov 30, 2014 · 16 comments
Closed

GroupBy + OrderBy not working #57

jtomek opened this issue Nov 30, 2014 · 16 comments

Comments

@jtomek
Copy link

jtomek commented Nov 30, 2014

Hi guys,

I can't get OrderBy to work with GroupBy.

ng-repeat="(key, value) in memories | groupBy: 'groupDate' | orderBy : '-groupDate'

orderBy does not work.

groupDate is a normal date (2014/11/29)
I also tried using value.data.groupDate[0]; Not working too.

Nothing seems to change the order.

Could you please advice?

Thanks and best regards,
Jakub

@a8m
Copy link
Owner

a8m commented Dec 1, 2014

Hi @jtomek , see issue #26
quote: orderBy must be the last filter in the chaining (when you apply filter to the result of another filter).
But, groupBy return an object, and orderBy expect array as an arguments.
So, you can do something like that:

$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'));
  }
 <ul ng-repeat="group in groups | groupBy:'category' | toArray:true | orderBy:min">
    <!-- 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>

RESULT:

- gamma
  - {"category":"gamma","id":0}
  - {"category":"gamma","id":1}
- alpha
  - {"category":"alpha","id":2}
  - {"category":"alpha","id":4}
- beta
  - {"category":"beta","id":3}
  - {"category":"beta","id":5}

@jtomek
Copy link
Author

jtomek commented Dec 1, 2014

Thank you very much for your reply!

I have already seen this entry. I think it does not fit to my problem.

All of my entries have a created_at date. I group these entries by month and year. I have for example the following groups:

Raw Input (random)
  • November 14,
  • October 13
  • December 14,
  • November 13,

I would like to get (newest groups first)

Expected Output
  • December 14
  • November 14
  • November 13
  • October 13

I understand that I can format the date in one way (2014/12, 2014/11, 2013/11, 2013/10) for the grouping and I can then format the view in a more readable way (December 14), that is no problem. So right now, the grouping is ordered like this:

Current Output
  • 2013/10
  • 2013/11
  • 2014/11
  • 2014/12

I would like to order the groups by -groupDate (reverse).

In future, I may want to add additional possibilities of grouping, such as numbers, Group names.

How to achieve this functionality?

I look Forward your reply,
Jakub

@jtomek
Copy link
Author

jtomek commented Dec 1, 2014

I figured out that the problem is connected to Masonry. When I call toArray, the view starts "blinking". Scope appears and disappears in 100 ms intervals. I am not quite sure how to solve it nicely.

Nevertheless, I came up with a workaround solution :-\

It is possible to do simple subscrations to reverse the native sorting.

I subtract the year of an entry from the current year. I subtract the month of the entry from the 12.

2014: 2014 - 2014 = 0, thus first.
2014-2013 = 1 gets second.

For months... I use max 12 - month of th entry.
For 2014 December: 2014-2014 and 12-12 = 0 0
For 2014 November: 2014-2014 and 12-11 = 0 1
For 2013 December: 2014-2013 and 12-12 = 1 0

Sorted alphabetically, I get it correct.

I am sorry, but I can't unfortunately get it work differently. I have already spent two days on this problem :-\

@a8m
Copy link
Owner

a8m commented Dec 2, 2014

@jtomek can you please provide some fiddle/jsbin example?

@a8m a8m closed this as completed Dec 2, 2014
@a8m a8m reopened this Dec 2, 2014
@RouR
Copy link

RouR commented Dec 9, 2014

@a8m please add the example to documentation

@a8m
Copy link
Owner

a8m commented Dec 9, 2014

Hi @RouR
Please, feel free to PR.

@RouR
Copy link

RouR commented Dec 9, 2014

@a8m can you check http://jsbin.com/maworugawe/1/watch?

@a8m
Copy link
Owner

a8m commented Dec 9, 2014

@RouR please, take a look on this example: jsbin

@RouR
Copy link

RouR commented Dec 9, 2014

thank you very much!

@a8m
Copy link
Owner

a8m commented Dec 9, 2014

@RouR Cheers.

@dyorg
Copy link

dyorg commented Dec 9, 2014

Can I contribute?

I replace _groupBy function to work with native orderBy. that's better, isn't it?

return filterWatcher.isMemoized('groupBy', arguments) ||
        filterWatcher.memoize('groupBy', arguments, this,
          _groupBy(collection, property));

function _groupBy (collection, key) {       
        var result = {};
        var indexes;
    indexes = [];

    forEach(collection, function(item) {
            var group_name = item[key];
            var index = indexes.indexOf(group_name);

            if (index == -1) {
                    index = indexes.push(group_name) - 1;
                    result[index] = {};
                    result[index].group_name = group_name;
                    result[index].items = [];
            }

            result[index].items.push(item);
     });
     return result;
};
<ul ng-repeat="task_group in tasks | orderBy : 'date' | groupBy : 'date'">
    <!-- print the group name -->
    <li>{{ task_group.group_name}}</li>
    <!-- iterate over the group tasks and order each group by name -->
    <li ng-repeat="task in task_group.items | orderBy:'name'">
      {{ task }}
    </li>
</ul>

@dyorg
Copy link

dyorg commented Dec 10, 2014

GroupBy filter complete with a improve

angular.module('a8m.group-by', [ 'a8m.filter-watcher' ])
  .filter('groupBy', [ '$parse', 'filterWatcher', function ( $parse, filterWatcher ) {
    return function (collection, property) {

      if(!isObject(collection) || isUndefined(property)) {
        return collection;
      }

      return filterWatcher.isMemoized('groupBy', arguments) ||
        filterWatcher.memoize('groupBy', arguments, this,
          _groupBy(collection, property));

      /**
       * groupBy function
       * @param collection
       * @param getter
       * @returns {{}}
       */
      function _groupBy (collection, key) {
            var result = {};
            var indexes;
            indexes = [];

            forEach(collection, function(item) {
                var group_name = item[key];
                var index = indexes.indexOf(group_name);

                if (index == -1) {
                    index = pad(indexes.push(group_name) - 1, 4);
                    result[index] = {};
                    result[index].group_name = group_name;
                    result[index].items = [];
                } else {
                    index = pad(index, 4);
                }

                result[index].items.push(item);
            });
            return result;
      };

      /**
       * padding function
       * @param number
       * @param length
       * @returns string
       */
      function pad (number, length) {
            var str = '' + number;
            while (str.length < length) {
                str = '0' + str;
            }
            return str;
      }
    };
 }]);

@evilaliv3
Copy link

@a8m : i've implemented the solution that you suggest in #57 (comment) but it appears to me to trigher a 100% cpu as probably the digest loop never finish. is there any alternative?

@evilaliv3
Copy link

ok it was my fault as im using it with nested forms inputs so that i've to benefit of the onetime binding (::) in order to evaluate it only once.

@rkingon
Copy link

rkingon commented Jul 28, 2016

I found the performance on the | toArray: true | orderBy: customMappingFunction method 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!

@chenyilei
Copy link

Hi @jtomek , see issue #26
quote: orderBy must be the last filter in the chaining (when you apply filter to the result of another filter).
But, groupBy return an object, and orderBy expect array as an arguments.
So, you can do something like that:

$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'));
  }
 <ul ng-repeat="group in groups | groupBy:'category' | toArray:true | orderBy:min">
    <!-- 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>

RESULT:

- gamma
  - {"category":"gamma","id":0}
  - {"category":"gamma","id":1}
- alpha
  - {"category":"alpha","id":2}
  - {"category":"alpha","id":4}
- beta
  - {"category":"beta","id":3}
  - {"category":"beta","id":5}

good for me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants