ฉันจะจัดกลุ่มข้อมูลด้วยตัวกรองเชิงมุมได้อย่างไร


136

ฉันมีรายชื่อผู้เล่นที่อยู่ในกลุ่มแต่ละคน ฉันจะใช้ตัวกรองเพื่อแสดงรายชื่อผู้ใช้ต่อกลุ่มได้อย่างไร

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

ฉันกำลังมองหาผลลัพธ์นี้:

  • ทีมอัลฟ่า
    • ยีน
  • เบต้าของทีม
    • จอร์จ
    • พอลล่า
  • แกมมาของทีม
    • สตีฟ
    • Scruath ของภาค 5

คำตอบ:


182

คุณสามารถใช้ groupBy ของโมดูลangular.filter
เพื่อให้คุณสามารถทำสิ่งนี้:

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
* player: Gene
Group name: beta
* player: George
* player: Paula
Group name: gamma
* player: Steve
* player: Scruath

UPDATE: jsbinจำข้อกำหนดพื้นฐานในการใช้งานangular.filterโดยเฉพาะโปรดทราบว่าคุณต้องเพิ่มลงในการอ้างอิงของโมดูลของคุณ:

(1) คุณสามารถติดตั้งตัวกรองเชิงมุมโดยใช้ 4 วิธีที่แตกต่างกัน:

  1. โคลนและสร้างที่เก็บนี้
  2. ผ่าน Bower: โดยเรียกใช้ $ bower ติดตั้งตัวกรองเชิงมุมจากเทอร์มินัลของคุณ
  3. ผ่าน npm: โดยเรียกใช้ $ npm ติดตั้งตัวกรองเชิงมุมจากเทอร์มินัลของคุณ
  4. ผ่าน cdnjs http://www.cdnjs.com/libraries/angular-filter

(2) รวม angular-filter.js (หรือ angular-filter.min.js) ใน index.html ของคุณหลังจากรวม Angular แล้ว

(3) เพิ่ม 'angular.filter' ในรายการการอ้างอิงของโมดูลหลักของคุณ


ตัวอย่างที่ดี อย่างไรก็ตามคีย์จะส่งคืนชื่อกลุ่มไม่ใช่คีย์จริง ... เราจะแก้ปัญหานั้นได้อย่างไร
JohnAndrews

7
อย่าลืมใส่angular.filterโมดูล
Puce

1
คุณสามารถใช้ order-by กับ group-by @erfling, PTAL ได้ที่: github.com/a8m/angular-filter/wiki/…
a8m

1
โอ้ว้าว. ขอบคุณ. ฉันไม่ได้คาดหวังว่าการสั่งให้ลูปที่ซ้อนกันส่งผลกระทบต่อวงนอกในลักษณะนั้น ที่มีประโยชน์จริงๆ +1
เกิดขึ้น

1
@Xyroid ฉันกำลังมองหาเหมือนกันฉันต้องการทำเป็นkeyวัตถุ ขอให้โชคดี
สุดยอด

25

นอกจากคำตอบที่ยอมรับข้างต้นแล้วฉันได้สร้างตัวกรอง 'groupBy' ทั่วไปโดยใช้ไลบรารี underscore.js

JSFiddle (อัพเดท): http://jsfiddle.net/TD7t3/

ตัวกรอง

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

จดบันทึกการโทร วิธีการขีดล่างนี้จะเก็บผลลัพธ์ของฟังก์ชันและหยุดเชิงมุมจากการประเมินนิพจน์ของตัวกรองทุกครั้งดังนั้นจึงป้องกันไม่ให้เชิงมุมถึงขีด จำกัด การวนซ้ำย่อย

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' ของเรากับตัวแปรขอบเขต teamPlayers ในคุณสมบัติ "ทีม" ng-repeat ของเราได้รับชุดค่าผสมของ (คีย์ค่า []) ที่เราสามารถใช้ในการทำซ้ำต่อไปนี้

อัปเดต 11 มิถุนายน 2557 ฉันขยายกลุ่มโดยใช้ตัวกรองเพื่อพิจารณาการใช้นิพจน์เป็นคีย์ (เช่นตัวแปรซ้อน) บริการแยกวิเคราะห์เชิงมุมมีประโยชน์มากสำหรับสิ่งนี้:

ตัวกรอง (พร้อมการสนับสนุนนิพจน์)

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/


คุณพูดถูกแล้วลิงค์ซอได้รับการอัปเดตแล้ว ขอบคุณที่แจ้งเข้ามา
chrisv

3
สวยเนี๊ยบจริง! จำนวนรหัสน้อยที่สุด
Benny Bottema

3
สิ่งหนึ่งที่ควรทราบเกี่ยวกับสิ่งนี้ - โดยค่าเริ่มต้นการบันทึกจะใช้พารามิเตอร์แรก (เช่น 'รายการ') เป็นคีย์แคชดังนั้นหากคุณส่ง 'รายการ' เดียวกันกับ 'ฟิลด์' ที่แตกต่างกันระบบจะส่งคืนค่าแคชเดียวกัน ยินดีต้อนรับโซลูชั่น
Tom Carver

ฉันคิดว่าคุณสามารถใช้ค่า $ id เพื่อหลีกเลี่ยงสิ่งนี้: item in items track by $ id (item)
Caspar Harmer

2
"คำตอบที่ยอมรับ" ข้อใด ใน Stack Overflow มีคำตอบที่ยอมรับได้เพียงคำตอบเดียว
Sebastian Mach

19

ขั้นแรกให้ทำวนซ้ำโดยใช้ตัวกรองที่จะส่งคืนเฉพาะทีมที่ไม่ซ้ำกันจากนั้นวนซ้ำที่ซ้อนกันซึ่งส่งคืนผู้เล่นทั้งหมดต่อทีมปัจจุบัน:

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

ง่ายมาก ที่ดี @Plantface.
Jeff Yates

ยอดเยี่ยมเพียง แต่จะเกิดอะไรขึ้นถ้าฉันต้องการส่งวัตถุใหม่ไปยัง $ scope ผู้เล่นเมื่อคลิก? เมื่อคุณวนลูปผ่านฟังก์ชั่นมันจะถูกเพิ่มหรือไม่?
เด็ดสุด ๆ

16

เดิมทีฉันใช้คำตอบของ Plantface แต่ฉันไม่ชอบวิธีที่ไวยากรณ์มองในมุมมองของฉัน

ฉันปรับปรุงใหม่เพื่อใช้$ q อ้างถึงหลังการประมวลผลข้อมูลและส่งคืนรายการในทีมที่ไม่ซ้ำกันซึ่งจะใช้เป็นตัวกรอง

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

});

1
คำตอบนี้ใช้ไม่ได้กับ AngularJS> 1.1 เนื่องจาก Promised จะไม่ถูกคลายออกอีกต่อไปสำหรับอาร์เรย์ ดูบันทึกการเข้าเมือง
Benny Bottema

6
ไม่จำเป็นต้องมีคำสัญญาในโซลูชันนี้เนื่องจากคุณไม่ได้ทำอะไรแบบอะซิงโครนัส ในกรณีนี้คุณสามารถข้ามขั้นตอนนั้นได้ ( jsFiddle )
Benny Bottema

11

คำตอบทั้งสองเป็นคำตอบที่ดีดังนั้นฉันจึงย้ายไปไว้ในคำสั่งเพื่อให้สามารถใช้ซ้ำได้และไม่จำเป็นต้องกำหนดตัวแปรขอบเขตที่สอง

นี่คือซอหากคุณต้องการเห็นการใช้งาน

ด้านล่างนี้คือคำสั่ง:

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>

11

อัปเดต

ผมเริ่มเขียนคำตอบนี้เพราะรุ่นเก่าของการแก้ปัญหาที่แนะนำโดยเอเรียลเมตรเมื่อรวมกับคนอื่น ๆ$filterที่เรียก" ข้อผิดพลาด Infite $ diggest ห่วง " (infdig ) โชคดีที่ปัญหานี้ได้รับการแก้ไขในรุ่นล่าสุดของangular.filter

ฉันแนะนำการใช้งานต่อไปนี้ซึ่งไม่มีปัญหานั้น :

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

อย่างไรก็ตามการใช้งานนี้จะใช้ได้เฉพาะกับเวอร์ชันก่อน Angular 1.3 (ฉันจะอัปเดตคำตอบนี้ในไม่ช้าโดยให้โซลูชันที่ใช้ได้กับทุกเวอร์ชัน)

ฉันได้จริงโพสต์เกี่ยวกับขั้นตอนที่ผมเอาไปพัฒนานี้$filterปัญหาที่ผมพบและสิ่งที่ผมได้เรียนรู้จากมัน


สวัสดี @Josep ดูangular-filterเวอร์ชั่นใหม่- 0.5.0 ไม่มีข้อยกเว้นอีกต่อไป groupByสามารถโซ่กับตัวกรองใดก็ได้ นอกจากนี้คุณยังทำกรณีทดสอบที่ยอดเยี่ยมได้สำเร็จ - นี่คือคำอธิบาย ขอบคุณ
a8m

1
@Josep มีปัญหาใน Angular 1.3
amcdnl

2

นอกเหนือจากคำตอบที่ยอมรับแล้วคุณยังสามารถใช้สิ่งนี้ได้หากคุณต้องการจัดกลุ่มตามหลายคอลัมน์ :

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.