build/rekord.js
function(grouping)
  {
    var by = createPropertyResolver( grouping.by );
    var having = createWhere( grouping.having, grouping.havingValue, grouping.havingEquals );
    var select = grouping.select || {};
    var map = {};

    if ( isString( grouping.by ) )
    {
      if ( !(grouping.by in select) )
      {
        select[ grouping.by ] = 'first';
      }
    }
    else if ( isArray( grouping.by ) )
    {
      for (var prop in grouping.by)
      {
        if ( !(prop in select) )
        {
          select[ prop ] = 'first';
        }
      }
    }

    for (var i = 0; i < this.length; i++)
    {
      var model = this[ i ];
      var key = by( model );
      var group = map[ key ];

      if ( !group )
      {
        group = map[ key ] = this.cloneEmpty();
      }

      group.add( model, true );
    }

    var groupings = this.cloneEmpty();

    groupings.setComparator( grouping.comparator, grouping.comparatorNullsFirst );

    for (var key in map)
    {
      var grouped = {};
      var groupArray = map[ key ];

      for (var propName in select)
      {
        var aggregator = select[ propName ];

        if ( isString( aggregator ) )
        {
          grouped[ propName ] = groupArray[ aggregator ]( propName );
        }
        else if ( isFunction( aggregator ) )
        {
          grouped[ propName ] = aggregator( groupArray, propName );
        }
      }

      if ( grouping.track !== false )
      {
        grouped.$group = groupArray;
      }

      if ( grouping.count !== false )
      {
        grouped.$count = groupArray.length;
      }

      if ( having( grouped, groupArray ) )
      {
        groupings.push( grouped );
      }
    }

    groupings.sort();

    return groupings;
  }
function(grouping)
  {
    var by = createPropertyResolver( grouping.by );
    var having = createWhere( grouping.having, grouping.havingValue, grouping.havingEquals );
    var select = grouping.select || {};
    var map = {};

    if ( isString( grouping.by ) )
    {
      if ( !(grouping.by in select) )
      {
        select[ grouping.by ] = 'first';
      }
    }
    else if ( isArray( grouping.by ) )
    {
      for (var prop in grouping.by)
      {
        if ( !(prop in select) )
        {
          select[ prop ] = 'first';
        }
      }
    }

    for (var i = 0; i < this.length; i++)
    {
      var model = this[ i ];
      var key = by( model );
      var group = map[ key ];

      if ( !group )
      {
        group = map[ key ] = this.cloneEmpty();
      }

      group.add( model, true );
    }

    var groupings = this.cloneEmpty();

    groupings.setComparator( grouping.comparator, grouping.comparatorNullsFirst );

    for (var key in map)
    {
      var grouped = {};
      var groupArray = map[ key ];

      for (var propName in select)
      {
        var aggregator = select[ propName ];

        if ( isString( aggregator ) )
        {
          grouped[ propName ] = groupArray[ aggregator ]( propName );
        }
        else if ( isFunction( aggregator ) )
        {
          grouped[ propName ] = aggregator( groupArray, propName );
        }
      }

      if ( grouping.track !== false )
      {
        grouped.$group = groupArray;
      }

      if ( grouping.count !== false )
      {
        grouped.$count = groupArray.length;
      }

      if ( having( grouped, groupArray ) )
      {
        groupings.push( grouped );
      }
    }

    groupings.sort();

    return groupings;
  }
function ( grouping ) { var by = createPropertyResolver ( grouping . by ) ; var having = createWhere ( grouping . having , grouping . havingValue , grouping . havingEquals ) ; var select = grouping . select || { } ; var map = { } ; if ( isString ( grouping . by ) ) { if ( ! ( grouping . by in select ) ) { select [ grouping . by ] = 'first' ; } } else if ( isArray ( grouping . by ) ) { for ( var prop in grouping . by ) { if ( ! ( prop in select ) ) { select [ prop ] = 'first' ; } } } for ( var i = 0 ; i < this . length ; i ++ ) { var model = this [ i ] ; var key = by ( model ) ; var group = map [ key ] ; if ( ! group ) { group = map [ key ] = this . cloneEmpty ( ) ; } group . add ( model , true ) ; } var groupings = this . cloneEmpty ( ) ; groupings . setComparator ( grouping . comparator , grouping . comparatorNullsFirst ) ; for ( var key in map ) { var grouped = { } ; var groupArray = map [ key ] ; for ( var propName in select ) { var aggregator = select [ propName ] ; if ( isString ( aggregator ) ) { grouped [ propName ] = groupArray [ aggregator ] ( propName ) ; } else if ( isFunction ( aggregator ) ) { grouped [ propName ] = aggregator ( groupArray , propName ) ; } } if ( grouping . track !== false ) { grouped . $group = groupArray ; } if ( grouping . count !== false ) { grouped . $count = groupArray . length ; } if ( having ( grouped , groupArray ) ) { groupings . push ( grouped ) ; } } groupings . sort ( ) ; return groupings ; }
Groups the elements into sub collections given some property expression to
use as the value to group by.

```javascript
var c = Rekord.collect([
{ name: 'Tom', age: 6, group: 'X' },
{ name: 'Jon', age: 7, group: 'X' },
{ name: 'Rob', age: 8, group: 'X' },
{ name: 'Bon', age: 9, group: 'Y' },
{ name: 'Ran', age: 10, group: 'Y' },
{ name: 'Man', age: 11, group: 'Y' },
{ name: 'Tac', age: 12, group: 'Z' }
]);

c.group({by: 'group'});
// [{group: 'X', $count: 3, $group: [...]},
//  {group: 'Y', $count: 3, $group: [...]},
//  {group: 'Z', $count: 1, $group: [.]}]

c.group({by: 'group', select: {age: 'avg', name: 'first'}});
// [{group: 'X', age: 7, name: 'Tom', $count: 3, $group: [...]},
//  {group: 'Y', age: 9, name: 'Bon', $count: 3, $group: [...]},
//  {group: 'Z', age: 12, name: 'Tac', $count: 1, $group: [.]}]

c.group({by: 'group', track: false, count: false});
// [{group: 'X'}, {group: 'Y'}, {group: 'Z'}]

var havingMoreThanOne = function(grouping, groupElements) {
return groupElements.length > 0;
};
c.group({by: 'group', select: {age: 'avg'}, comparator: '-age', having: havingMoreThanOne, track: false, count: false});
// [{group: 'Y', age: 9},
//  {group: 'X', age: 7}]
```

@method
@memberof Rekord.Collection#
@param {Object} grouping -
An object specifying how elements in this collection are to be grouped
and what properties from the elements should be aggregated in the
resulting groupings.
- `by`: A property expression that resolves how elements will be grouped.
- `select`: An object which contains properties that should be aggregated where the value is the aggregate collection function to call (sum, avg, count, first, last, etc).
- `having`: A having expression which takes a grouping and the grouped elements and determines whether the grouping should be in the final result.
- `comparator`: A comparator for sorting the resulting collection of groupings.
- `comparatorNullsFirst`: Whether nulls should be sorted to the top.
- `track`: Whether all elements in the group should exist in a collection in the `$group` property of each grouping.
- `count`: Whether the number of elements in the group should be placed in the `$count` property of each grouping.
@return {Rekord.Collection} -
A collection of groupings.
Groups the elements into sub collections given some property expression to use as the value to group by .