2

I get an object like following:

$scope.Students = [{
    "Name": "Muffin 1",
    "RollNo": "12312",
    "Class": "Standard B",
    "Performances": [{
      "Group": "Science",
      "Subject": "Math",
      "Mark": 99
    }, {
      "Group": "Science",
      "Subject": "Physics",
      "Mark": 99
    }, {
      "Group": "Arts",
      "Subject": "Drawing",
      "Mark": 99
    }, {
      "Group": "Arts",
      "Subject": "Poetry",
      "Mark": 99
    }]
  }, {
    "Name": "Muffin 2",
    "RollNo": "1232",
    "Class": "Standard A",
    "Performances": [{
      "Group": "Science",
      "Subject": "Chemistry",
      "Mark": 99
    }, {
      "Group": "Science",
      "Subject": "Physics",
      "Mark": 90
    }, {
      "Group": "Arts",
      "Subject": "Drawing",
      "Mark": 99
    },  {
      "Group": "Commerce",
      "Subject": "Marketing",
      "Mark": 99
    }]
  }];

And I want to create a table like this:

enter image description here

Fixed known value for Performances.Group is: Science, Arts or Commerce But Subject and Mark is always variable. So What I have to do is putting all subject name in left column and then if student has mark for that subject put it in students column. Could any one please help me to solve this problem! Here is Plunker Link : http://plnkr.co/edit/vvLL00crhwSq4I6pjjUx?p=preview

Although, I could filter all the performances by Group but then I am confused how do I include all the subject Names in left column and then values accordingly as they are in separate arrays.

2
  • What exactly are you trying to do? You seemed to have lost me in the details... Commented Nov 13, 2014 at 16:09
  • If you have the power to do so, "group" should be a container in the JSON for "subject" and "mark" based on how you're using it. Commented Nov 13, 2014 at 16:14

2 Answers 2

1

This pretty much meets your needs:

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

var app = angular.module('plunker', []);

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

  $scope.studentsFiltered = $filter('groupByCustom')($scope.Students);
  console.log('restructured', $scope.studentsFiltered); 

});

Now you want a way to restructure the data, in a reusable way, I'm thinking a filter does well.

app.filter('groupByCustom', function(){
  return function(studentsList){
    console.log('studentsList', studentsList);
    // given a list of students, group into classes
    // final structure should be object which contains a different nesting
    /* we want:
       var grouping = {
         Science: {
           Math: {muffin 1: 99, muffin 2: 99},
           Chemistry: {muffin 1: 99, muffin 2: 99}
           // null if student is not in class
         }
       }
    */
    var newStructure = {};
    angular.forEach(studentsList, function(student, index){
      var name = student.Name;
      var performances = student.Performances;
      angular.forEach(performances, function(perf, ind){
        var group = perf.Group;
        var mark = perf.Mark;
        var sub = perf.Subject;

        if(!newStructure[group]){
          newStructure[group] = {};
        }
        if(!newStructure[group][sub]){
          newStructure[group][sub] = {};
          angular.forEach(studentsList, function(student, index){
            if(!newStructure[group][sub][student.name]){
              newStructure[group][sub][student.Name] = null;
            }
          });
        }
        if(!newStructure[group][sub][name]){
          newStructure[group][sub][name] = mark; 
        }
      });
    });
    angular.forEach(newStructure, function(k,v){
      angular.forEach(v, function(k,v){

      });
    });

    // console.log('newStructure', newStructure);
    return newStructure;
  };
});

Then you can build the HTML:

<tbody ng-repeat="(group, data) in studentsFiltered">
  <tr >
    <th class="section">Group</th>
    <th class="section" colspan={{Students.length}}>{{group}}</th>
  </tr>
  <tr ng-repeat="(class, students) in data">
    <td>{{class}}</td> 
    <td ng-repeat="(name, score) in students">{{score}}</td>
  </tr>
</tbody>
Sign up to request clarification or add additional context in comments.

2 Comments

thank you very much for your answer. I am trying to understand your code. Could you please explain me how do these commands work newStructure[group], newStructure[group][sub] or newStructure[group][sub][student.name]. I tried to figure it out but seems very confusing. As I am a beginner. In addition, Since it is reusable, If I want to show only subjects of 'Arts' how do I filter it, I tried using normal filter like: ng-repeat="(group, data) in studentsFiltered|filter: group='Arts'" but did not work. Thank you!
Basically, the structure as you have it doesn't support filtering naturally the way you want it, you need to restructure it so that it is easier to use in a repeater. newStructure is an object, and when you put a [] at the end of it you are trying to access it like an array, hence newStructure[group] assigns a key onto the object (which really becomes a hashmap the way we use it). And the value of group can be Math, Science, ... so you get {Math: {}, Science: {} }, which is easier to parse in your repeater.
1

First you have to reform your data in a structure which is easy to templates (plunker):

var rows = [[],[],[]]

rows[0].title = 'Student Name';
rows[1].title = 'Roll No';
rows[2].title = 'Class';

var groups = {};
var cols = $scope.Students.length;

angular.forEach($scope.Students , function(student, idx){

  rows[0].push(student.Name);
  rows[1].push(student.RollNo);
  rows[2].push(student.Class);

  angular.forEach(student.Performances , function(p) {

    // collect groups
    groups[p.Group] = groups[p.Group] || {};

    // collect subjects per group
    groups[p.Group][p.Subject] = groups[p.Group][p.Subject] || Array(cols);

    // collect marks per each group subject
    groups[p.Group][p.Subject][idx] = p.Mark;
  });
});

$scope.rows = rows;
$scope.groups = groups;

Then you can easily iterate the items:

  <table class="table table-bordered">
    <tr ng-repeat="row in rows">
      <th>{{row.title}}</th>
      <th ng-repeat="cell in row track by $index">{{cell}}</th>
    </tr>

    <tbody ng-repeat="(group, sections) in groups">
      <tr>
        <th>Group</th>
        <th colspan={{Students.length}}>{{group}}</th>
      </tr>
      <tr ng-repeat="(section, values) in sections">
        <th>{{section}}</th>
        <th ng-repeat="value in values track by $index">{{value}}</th>
      </tr>
    </tbody>
  </table>

3 Comments

dang, you beat me to the punch. I did something very similar
Well, the only thing I'd say you're missing is re-usability, if you wrap that in a directive/service/filter then you're good to go.
@llanFrumer Thank you very much for your answer. Your answer contains less code which I liked very much, Could you please explain this lines groups[p.Group] = groups[p.Group] || {}; groups[p.Group][p.Subject] = groups[p.Group][p.Subject] || Array(cols); groups[p.Group][p.Subject][idx] = p.Mark; I am confused how do they work. And If I want to show only subjects of 'Arts' how do I filter it, I tried using normal filter like: ng-repeat="(group, sections) in groups|filter: group='Arts'" but did not work. Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.