如何使用 Angular 过滤器对数据进行分组?

IT技术 javascript filter angularjs grouping
2021-01-29 22:54:12

我有一个玩家列表,每个玩家都属于一个组。如何使用过滤器列出每个组的用户?

[{name: 'Gene', team: 'team alpha'},
 {name: 'George', team: 'team beta'},
 {name: 'Steve', team: 'team gamma'},
 {name: 'Paula', team: 'team beta'},
 {name: 'Scruath of the 5th sector', team: 'team gamma'}];

我正在寻找这个结果:

  • 团队阿尔法
    • 基因
  • 团队测试版
    • 乔治
    • 宝拉
  • 团队伽马
    • 史蒂夫
    • 第 5 部门的 Scruath
6个回答

您可以使用angular.filtermodulegroupBy
所以你可以做这样的事情:

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 ng-repeat="(key, value) in players | groupBy: 'team'">
  Group name: {{ key }}
  <li ng-repeat="player in value">
    player: {{ player.name }} 
  </li>
</ul>

结果:
组名:alpha
* 玩家:Gene
组名:beta
* 玩家:George
* 玩家:Paula
组名:gamma
* 玩家:Steve
* 玩家:Scruath

更新: jsbin记住使用的基本要求angular.filter,特别注意你必须将它添加到你的module的依赖项中:

(1) 您可以使用 4 种不同的方法安装 angular-filter:

  1. 克隆并构建此存储库
  2. 通过 Bower:通过从终端运行 $ bower install angular-filter
  3. 通过 npm:通过从终端运行 $ npm install angular-filter
  4. 通过 cdnjs http://www.cdnjs.com/libraries/angular-filter

(2) 在 index.html 中包含 angular-filter.js(或 angular-filter.min.js),在包含 Angular 本身之后。

(3) 将 'angular.filter' 添加到主module的依赖项列表中。

哇哦。谢谢。我没想到订购嵌套循环会以这种方式影响外部循环。这真的很有用。+1
2021-03-17 22:54:12
您可以使用 order-by 和 group-by @erfling,PTAL 在:github.com/a8m/angular-filter/wiki/...
2021-03-18 22:54:12
很好的例子。但是,键返回的是组名而不是实际的键……我们该如何解决?
2021-03-29 22:54:12
@Xyroid 即使我正在寻找与我想制作key的对象相同的东西。任何运气从你身边
2021-04-02 22:54:12
不要忘记包含angular.filtermodule。
2021-04-08 22:54:12

除了上面接受的答案之外,我还使用 underscore.js 库创建了一个通用的“groupBy”过滤器。

JSFiddle(更新):http : //jsfiddle.net/TD7t3/

过滤器

app.filter('groupBy', function() {
    return _.memoize(function(items, field) {
            return _.groupBy(items, field);
        }
    );
});

注意 'memoize' 调用。此下划线方法缓存函数的结果并停止 angular 每次评估过滤器表达式,从而防止 angular 达到摘要迭代限制。

html

<ul>
    <li ng-repeat="(team, players) in teamPlayers | groupBy:'team'">
        {{team}}
        <ul>
            <li ng-repeat="player in players">
                {{player.name}}
            </li>
        </ul>
    </li>
</ul>

我们将“groupBy”过滤器应用于“team”属性上的 teamPlayers 范围变量。我们的 ng-repeat 接收一个 (key, values[]) 的组合,我们可以在接下来的迭代中使用它。

2014 年 6 月 11 日更新 我通过过滤器扩展了组以考虑使用表达式作为键(例如嵌套变量)。角度解析服务为此非常方便:

过滤器(带表达式支持)

app.filter('groupBy', function($parse) {
    return _.memoize(function(items, field) {
        var getter = $parse(field);
        return _.groupBy(items, function(item) {
            return getter(item);
        });
    });
});

控制器(带有嵌套对象)

app.controller('homeCtrl', function($scope) {
    var teamAlpha = {name: 'team alpha'};
    var teamBeta = {name: 'team beta'};
    var teamGamma = {name: 'team gamma'};

    $scope.teamPlayers = [{name: 'Gene', team: teamAlpha},
                      {name: 'George', team: teamBeta},
                      {name: 'Steve', team: teamGamma},
                      {name: 'Paula', team: teamBeta},
                      {name: 'Scruath of the 5th sector', team: teamGamma}];
});

html(带有 sortBy 表达式)

<li ng-repeat="(team, players) in teamPlayers | groupBy:'team.name'">
    {{team}}
    <ul>
        <li ng-repeat="player in players">
            {{player.name}}
        </li>
    </ul>
</li>

JSFiddle:http : //jsfiddle.net/k7fgB/2/

为什么在它不是 nesc 时包含下划线作为 dep
2021-03-17 22:54:12
@TomCarver 您可以使用解析器函数作为 _.memoize 的第二个参数来创建包含“字段”的缓存键。这个函数让你定义自己的缓存键。例如_.memoize( ... , function (items, field) { return _.pluck(items,'name').toString() + '-' + field})这个 SO 答案有更多细节:stackoverflow.com/a/16513030/490592
2021-03-17 22:54:12
有一点需要注意——默认情况下,memoize 使用第一个参数(即“items”)作为缓存键——所以如果你将相同的“items”传递给不同的“field”,它将返回相同的缓存值。欢迎解决。
2021-03-20 22:54:12
我认为你可以使用 $id 值来解决这个问题: item in items track by $id(item)
2021-03-31 22:54:12

首先使用过滤器进行循环,只返回唯一的球队,然后嵌套循环,返回每个当前球队的所有球员:

http://jsfiddle.net/plantface/L6cQN/

html:

<div ng-app ng-controller="Main">
    <div ng-repeat="playerPerTeam in playersToFilter() | filter:filterTeams">
        <b>{{playerPerTeam.team}}</b>
        <li ng-repeat="player in players | filter:{team: playerPerTeam.team}">{{player.name}}</li>        
    </div>
</div>

脚本:

function Main($scope) {
    $scope.players = [{name: 'Gene', team: 'team alpha'},
                    {name: 'George', team: 'team beta'},
                    {name: 'Steve', team: 'team gamma'},
                    {name: 'Paula', team: 'team beta'},
                    {name: 'Scruath of the 5th sector', team: 'team gamma'}];

    var indexedTeams = [];

    // this will reset the list of indexed teams each time the list is rendered again
    $scope.playersToFilter = function() {
        indexedTeams = [];
        return $scope.players;
    }

    $scope.filterTeams = function(player) {
        var teamIsNew = indexedTeams.indexOf(player.team) == -1;
        if (teamIsNew) {
            indexedTeams.push(player.team);
        }
        return teamIsNew;
    }
}
太棒了。但是如果我想在点击时将一个新对象推送到 $scope.players 呢?当你循环一个函数时,它会被添加吗?
2021-03-11 22:54:12

我最初使用 Plantface 的答案,但我不喜欢语法在我看来的样子。

我对其进行了修改,以使用$q.defer对数据进行后处理并返回一个关于独特团队的列表,然后将其用作过滤器。

http://plnkr.co/edit/waWv1donzEMdsNMlMHBa?p=preview

看法

<ul>
  <li ng-repeat="team in teams">{{team}}
    <ul>
      <li ng-repeat="player in players | filter: {team: team}">{{player.name}}</li> 
    </ul>
  </li>
</ul>

控制器

app.controller('MainCtrl', function($scope, $q) {

  $scope.players = []; // omitted from SO for brevity

  // create a deferred object to be resolved later
  var teamsDeferred = $q.defer();

  // return a promise. The promise says, "I promise that I'll give you your
  // data as soon as I have it (which is when I am resolved)".
  $scope.teams = teamsDeferred.promise;

  // create a list of unique teams. unique() definition omitted from SO for brevity
  var uniqueTeams = unique($scope.players, 'team');

  // resolve the deferred object with the unique teams
  // this will trigger an update on the view
  teamsDeferred.resolve(uniqueTeams);

});
此答案不适用于 AngularJS > 1.1,因为 Promised 不再针对数组展开。移民注意事项
2021-03-20 22:54:12
此解决方案中不需要 Promise,因为您没有异步执行任何操作。在这种情况下,您可以简单地跳过该步骤 ( jsFiddle )。
2021-03-25 22:54:12

两个答案都很好,所以我将它们移到了一个指令中,这样它就可以重用并且不必定义第二个作用域变量。

如果你想看到它的实现,这是小提琴

下面是指令:

var uniqueItems = function (data, key) {
    var result = [];
    for (var i = 0; i < data.length; i++) {
        var value = data[i][key];
        if (result.indexOf(value) == -1) {
            result.push(value);
        }
    }
    return result;
};

myApp.filter('groupBy',
            function () {
                return function (collection, key) {
                    if (collection === null) return;
                    return uniqueItems(collection, key);
        };
    });

那么它可以如下使用:

<div ng-repeat="team in players|groupBy:'team'">
    <b>{{team}}</b>
    <li ng-repeat="player in players | filter: {team: team}">{{player.name}}</li>        
</div>