Ember.js Grouping Results

ember.js

Solution

Here's a Groupable implementation I've adapted a bit:

Groupable = Ember.Mixin.create
  group: null
  ungroupedContent: null

  groupedContent: (->
    model = @
    groupedContent = Ember.A([])

    groupCallback = @get('group')
    ungroupedContent = @get('ungroupedContent')

    return groupedContent unless groupCallback
    return groupedContent unless ungroupedContent

    ungroupedContent.forEach (item) ->
      group = groupCallback.call(model, item)
      return unless groupKey = group.get('key')

      foundGroup = groupedContent.findProperty('group.key', groupKey)

      unless foundGroup
        foundGroup = groupedContent.pushObject Ember.ArrayProxy.create
          group: group,
          content: Ember.A([])

      foundGroup.get('content').pushObject(item)

    groupedContent
  ).property('group', 'ungroupedContent.@each')

Controller usage:

ActivitiesController = Ember.ArrayController.extend Groupable,
  ungroupedContentBinding: 'content' # tell Groupable where your default content is

  # the callback that will be called for every
  # item in your content array -
  # just return the same 'key' to put it in the same group
  group: (activity) ->
    Ember.Object.create
      key: moment.utc(activity.get('date')).format('YYYY-MM-DD') # using momentjs to pluck the day from the date
      description: 'some string describing this group (if you want)'

Template usage:

{{#each groupedContent}}
  {{group.key}} - {{group.description}}

  {{#each content}}
    Here's the really bad thing that happened: {{message}}
  {{/each}}
{{/each}}

Essentially, all that the Groupable mixin does is loop through your normal content ArrayProxy and calls a function to determine what group it belongs to. If that group object doesn't already exist (i.e. no matching `group.key` was found), it creates it before adding the current content item to the group's content.

So what you end up with in your template is a new array (`ArrayProxy`) of objects (`Ember.Object`), with each object having a `group` property (which must have at least a `key` property to identify it) and a `content` property (which contains the content belonging to that group).

Problem

I'm trying to achieve a "group by" functionality in Ember.js and its proving more difficult than I originally thought. ``` [{date: '2014-01-15T16:22:16-08:00', message: 'Tidal wave occurred'}, {date: '2014-01-15T05:00:16-08:00', message: 'Tornado destroyed town'}, {date: '2014-01-13T14:22:16-08:00', message: 'Volcanic eruption, likely'}, {date: '2014-01-13T16:24:16-08:00', message: 'Ice shelf calving off completely'}, {date: '2014-01-11T11:27:26-08:00', message: 'Mother-in-law visiting'}] ``` I'm looking for final output similar to this: ``` Today ---------- 4:22 pm - Tidal wave occurred 5:00 am - Tornado destroyed town Monday ---------- 2:22 pm - Volcanic eruption, likely ...etc., etc. ``` Right now this data is bound to an ArrayController. Each object has a computed property to get "year/month/day" for the grouping comparison. ``` var ActivityController = Em.ArrayController.extend({ groupedResults: function () { var result = []; //Iterate over each item in the list, but do what with it? this.get('content').filter(function (item) { }); return result; }.property('content.[]') }); ``` Any tips on the best way to implement this? Ember ships with filterBy and mapBy, but no groupBy.

Original source