Açısal bir filtreyle verileri nasıl gruplandırabilirim?


136

Her bir gruba ait oyuncuların bir listesi var. Grup başına kullanıcıları listelemek için bir filtreyi nasıl kullanabilirim?

[{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'}];

Bu sonucu arıyorum:

  • takım alfa
    • Gen
  • takım beta
    • George
    • Paula
  • takım gamması
    • Steve
    • 5. sektörün pasajı

Yanıtlar:


182

Sen GroupBy kullanabilirsiniz angular.filter modül.
böylece böyle bir şey yapabilirsiniz:

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>

SONUÇ:
Grup adı: alfa
* oyuncu: Gene
Grup adı: beta
* oyuncu: George
* oyuncu: Paula
Grup adı: gama
* oyuncu: Steve
* oyuncu: Scruath

GÜNCELLEME: jsbin Kullanılacak temel gereksinimleri hatırlayın angular.filter, özellikle modülünüzün bağımlılıklarına eklemeniz gerektiğini unutmayın:

(1) Açısal filtreyi 4 farklı yöntem kullanarak kurabilirsiniz:

  1. bu havuzu kopyala ve oluştur
  2. Bower üzerinden: $ bower çalıştırarak terminalinizden açısal filtre kurun
  3. npm ile: $ npm çalıştırarak terminalinizden açısal filtre kurun
  4. cdnjs üzerinden http://www.cdnjs.com/libraries/angular-filter

(2) Angular'ın kendisini ekledikten sonra index.html dosyasına angular-filter.js (veya angular-filter.min.js) ekleyin.

(3) Ana modülünüzün bağımlılık listesine 'angular.filter' ekleyin.


Harika bir örnek. Ancak, anahtar gerçek anahtar değil grup adını döndürür ... bunu nasıl çözebiliriz?
JohnAndrews

7
angular.filterModülü dahil etmeyi unutmayın .
Puce

1
group-by @ erfling, PTAL ile şu
adresten kullanabilirsiniz

1
Vay canına. Teşekkürler. İç içe döngü sipariş dış bir şekilde bu şekilde etkilemek için beklemiyorduk. Bu gerçekten faydalı. +1
16'da erfling

1
@ Xyroid bile keynesne olarak yapmak istiyorum aynı arıyorum . yanınızdan herhangi bir şans
süper serin

25

Yukarıdaki kabul edilen cevaplara ek olarak underscore.js kütüphanesini kullanarak genel bir 'groupBy' filtresi oluşturdum.

JSFiddle (güncellendi): http://jsfiddle.net/TD7t3/

Filtre

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

'Not' çağrısına dikkat edin. Bu alt çizgi yöntemi, işlevin sonucunu önbelleğe alır ve her seferinde filtre ifadesinin değerlendirilmesini engelleyerek, açısalın özet yineleme sınırına ulaşmasını önler.

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' filtremizi teamPlayers kapsam değişkenine, 'team' özelliğine uygularız. Ng tekrarımız, aşağıdaki yinelemelerde kullanabileceğimiz bir (anahtar, değerler []) kombinasyonu alır.

Güncelleme 11 Haziran 2014 Anahtar olarak ifadelerin kullanımını (örneğin iç içe değişkenler) hesaba katmak için grubu filtreye göre genişlettim. Açısal ayrıştırma hizmeti bunun için oldukça kullanışlı:

Filtre (ifade destekli)

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

Denetleyici (iç içe nesnelerle)

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 ifadesiyle)

<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/


Haklısın, keman linki güncellendi. Beni bilgilendirdiğiniz için teşekkür ederim.
chrisv

3
Bu aslında oldukça düzenli! En az miktarda kod.
Benny Bottema

3
Bununla dikkat edilmesi gereken bir şey - varsayılan olarak memoize, önbellek anahtarı olarak ilk parametreyi (yani 'öğeler') kullanır - bu yüzden farklı bir 'alanıyla aynı' öğeleri 'iletirseniz, aynı önbellek değerini döndürür. Çözümler hoş geldiniz.
Tom Carver

Bence bu
sorunu çözmek

2
Hangi "kabul edilen cevaplar"? Yığın Taşması'nda yalnızca bir kabul edilen cevap olabilir.
Sebastian Mach

19

Önce yalnızca benzersiz ekipleri döndürecek bir filtre ve ardından mevcut ekip başına tüm oyuncuları döndüren iç içe bir döngü kullanarak bir döngü yapın:

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>

senaryo:

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;
    }
}

Çok basit. Güzel bir @ Bitki.
Jeff Yates

sadece parlak. ama yeni bir nesneyi $ scope.players'a tıklamak istiyorsam ne olur? u bir işlevi üzerinden döngü olarak eklenecek?
Süper serin

16

Başlangıçta Plantface'in cevabını kullandım, ancak sözdiziminin görüşüme göre nasıl göründüğünü beğenmedim.

Verileri sonradan işlemek ve daha sonra filtre olarak kullanılan benzersiz ekiplerde bir liste döndürmek için $ q.defer kullanmak için yeniden çalıştım .

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

Görünüm

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

kontrolör

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);

});

1
Bu cevap AngularJS> 1.1 ile çalışmıyor çünkü Promised diziler için paketlenmemiş. Göçmenlik notlarına
Benny Bottema

6
Eşzamansız bir şey yapmadığınız için bu çözümde Promise gerek yoktur. Bu durumda, bu adımı atlayabilirsiniz ( jsFiddle ).
Benny Bottema

11

Her iki cevap da iyiydi, bu yüzden onları yeniden kullanılabilir ve ikinci bir kapsam değişkeninin tanımlanmasına gerek kalmayacak şekilde bir direktife taşıdım.

İşte uygulandığını görmek istiyorsanız keman

Direktif aşağıdadır:

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);
        };
    });

Sonra aşağıdaki gibi kullanılabilir:

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

11

Güncelleme

Bu cevabı başlangıçta yazdım çünkü Ariel M. tarafından önerilen çözümün eski sürümü, diğer $filters ile birleştirildiğinde bir " Infite $ diggest Loop Error " ( infdig) tetikledi . Neyse ki bu sorun angular.filter dosyasının en son sürümünde çözülmüştür .

Ben bu sorunu yoktu aşağıdaki uygulamayı önerdi :

angular.module("sbrpr.filters", [])
.filter('groupBy', function () {
  var results={};
    return function (data, key) {
        if (!(data && key)) return;
        var result;
        if(!this.$id){
            result={};
        }else{
            var scopeId = this.$id;
            if(!results[scopeId]){
                results[scopeId]={};
                this.$on("$destroy", function() {
                    delete results[scopeId];
                });
            }
            result = results[scopeId];
        }

        for(var groupKey in result)
          result[groupKey].splice(0,result[groupKey].length);

        for (var i=0; i<data.length; i++) {
            if (!result[data[i][key]])
                result[data[i][key]]=[];
            result[data[i][key]].push(data[i]);
        }

        var keys = Object.keys(result);
        for(var k=0; k<keys.length; k++){
          if(result[keys[k]].length===0)
            delete result[keys[k]];
        }
        return result;
    };
});

Ancak, bu uygulama yalnızca Açısal 1.3'ten önceki sürümlerle çalışacaktır. (Bu yanıtı tüm sürümlerle çalışan bir çözüm sağlayarak kısa süre içinde güncelleyeceğim.)

Aslında bunu geliştirmek için attığım adımlar, $filterkarşılaştığım sorunlar ve ondan öğrendiğim şeyler hakkında bir yazı yazdım .


Merhaba @Josep, yeni angular-filtersürüme bir göz atın - 0.5.0, artık bir istisna yok. groupByherhangi bir filtre ile zincir olabilir. Ayrıca, harika test senaryoları başarıyla bitiyor - burada bir dalgıç Teşekkürler.
a8m

1
@Josep Açısal 1.3'te sorun
yaşıyor

2

Kabul edilen cevaba ek olarak, birden çok sütuna göre gruplamak istiyorsanız bunu kullanabilirsiniz :

<ul ng-repeat="(key, value) in players | groupBy: '[team,name]'">

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.