6

I'm looking way to do something like

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 repeat.for="obj of players | groupBy: 'team'">
  Group name: ${obj.group}
  <li repeat.for="player of obj.values">
    player: ${player.name} 
  </li>
</ul>

Is it possible to do? Or what the better way to do this logic in Aurelia way?

Egor Malkevich
  • 1,516
  • 1
  • 11
  • 24

3 Answers3

5

You can do this using a ValueConverter.

export class GroupByValueConverter {
    toView(array, groupBy) {

        var groups = {};

        array.forEach(function (o) {
            var group = o[groupBy];
            groups[group] = groups[group] || [];
            groups[group].push(o);
        });

        return Object.keys(groups).map(function (group) {
            return {
                group: group,
                values: groups[group]
            };
        })
    }
}
Leo
  • 7,379
  • 6
  • 28
  • 44
1

After finding this answer, I did it in a slightly different way. Instead of using a an array of objects with group and value keys, I used a Map.

Updated view

<ul repeat.for="[group, values] of players | groupBy:'team'">
  Group name: ${group}
  <li repeat.for="player of values">
    player: ${player.name} 
  </li>
</ul>

For the value converter I used this answer for inspiration on an efficient way to perform a group by operation.

Value Converter

export class GroupByValueConverter {
  toView(objects, key) {
    return objects.reduce(
      (rv, x) => rv.set(x[key], (rv.get(x[key]) || []).concat(x)), 
      new Map()
    ); 
  }
}
Community
  • 1
  • 1
Dave Jensen
  • 4,574
  • 1
  • 40
  • 45
1

An extension to the ValueConverter above allowing to use a grouping filter with nested object properties (eg. groupBy:'team.id')

export class GroupByValueConverter {
    toView(array, groupBy) {

        var groups = {};
        var props = groupBy.split(".");

        array.forEach(function (o) {
            var group = o;
            props.forEach(function (p) { group = group[p] });
            groups[group] = groups[group] || [];
            groups[group].push(o);
        });

        return Object.keys(groups).map(function (group) {
            return {
                group: group,
                values: groups[group],
            };
        })
    }
}

Yet another extension that allows to specify as group an object. It takes a second parameter for specifying the object key to be used as indexer.

eg. - | groupBy:'team':'id' - | groupBy:'projectMember.team':'id'

export class GroupByValueConverter {
    toView(array, groupBy, groupByKey) {

        var groups = {};
        var groupMembers = {};
        var props = groupBy.split(".");

        array.forEach(function (o) {
            var group = o;
            props.forEach(function (p) { group = group[p] });
            var index = groupByKey && group ? group[groupByKey] : group;
            groups[index] = group;
            groupMembers[index] = groupMembers[index] || [];
            groupMembers[index].push(o);
        });

        return Object.keys(groups).map(function (index) {
            return {
                group: groups[index],
                values: groupMembers[index],
            };
        })
    }
}
Kathleen
  • 133
  • 1
  • 7