Convert an array of JavaScript objects

I have the following two JavaScript arrays:

var grades = [ { name: "A", color: "#00FF00" }, 
               { name: "B", color: "#88CC00" },
               { name: "C", color: "#AAAA00" }, 
               { name: "D", color: "#CC8800" }, 
               { name: "F", color: "#FF0000" }];

var studentGrades = [ { Student: "James", Class: "Math", Grade: "A" },
                      { Student: "Lily", Class: "Math", Grade: "B" },
                      { Student: "Bob", Class: "Math", Grade: "C" },
                      { Student: "Tom", Class: "Math", Grade: "C" },
                      { Student: "James", Class: "Science", Grade: "A" },
                      { Student: "Lily", Class: "Science", Grade: "B" },
                      { Student: "Bob", Class: "Science", Grade: "B" },
                      { Student: "Tom", Class: "Science", Grade: "B" },
                      { Student: "James", Class: "Chemistry", Grade: "F" },
                      { Student: "Lily", Class: "Chemistry", Grade: "A" },
                      { Student: "Bob", Class: "Chemistry", Grade: "B" },
                      { Student: "Tom", Class: "Chemistry", Grade: "A" } ];

      

And I'm trying to create the following two arrays from it:

grades = [ { name: "A", color: "#00FF00", data: [1, 1, 2] },
                    { name: "B", color: "#88CC00", data: [1, 3, 1] },
                    { name: "C", color: "#AAAA00", data: [2, 0, 0] },
                    { name: "D", color: "#CC8800", data: [0, 0, 0] },
                    { name: "F", color: "#FF0000", data: [0, 0, 1] } ];

var classes = [ "Math", "Science", "Chemistry" ];

      

Is there an easy way to do this without looping over studentGrades and maintaining counts and a unique list of classes?

Edit: After posting the question, I created the following code. I would prefer an easier way to do this, which is a bit readable.

<!DOCTYPE html>
<html>
  <head>
    <base href="http://demos.telerik.com/kendo-ui/bar-charts/local-data-binding">
    <style>html { font-size: 12px; font-family: Arial, Helvetica, sans-serif; }</style>
    <title></title>
    <link rel="stylesheet" href="http://cdn.kendostatic.com/2015.1.429/styles/kendo.common-bootstrap.min.css" />
    <link rel="stylesheet" href="http://cdn.kendostatic.com/2015.1.429/styles/kendo.bootstrap.min.css" />
    <link rel="stylesheet" href="http://cdn.kendostatic.com/2015.1.429/styles/kendo.dataviz.min.css" />
    <link rel="stylesheet" href="http://cdn.kendostatic.com/2015.1.429/styles/kendo.dataviz.bootstrap.min.css" />

    <script src="http://cdn.kendostatic.com/2015.1.429/js/jquery.min.js"></script>
    <script src="http://cdn.kendostatic.com/2015.1.429/js/kendo.all.min.js"></script>
  </head>
  <body>
    <div id="example">
      <div class="demo-section k-content">
        <div id="chart"></div>
      </div>
      <script>

        // Start of code that is specific to the stackoverflow question

        var grades = [ { name: "A", color: "#00FF00" }, 
                       { name: "B", color: "#88CC00" },
                       { name: "C", color: "#AAAA00" }, 
                       { name: "D", color: "#CC8800" }, 
                       { name: "F", color: "#FF0000" }];

        var studentGrades = [ { Student: "James", Class: "Math", Grade: "A" },
                              { Student: "Lily", Class: "Math", Grade: "B" },
                              { Student: "Bob", Class: "Math", Grade: "C" },
                              { Student: "Tom", Class: "Math", Grade: "C" },
                              { Student: "James", Class: "Science", Grade: "A" },
                              { Student: "Lily", Class: "Science", Grade: "B" },
                              { Student: "Bob", Class: "Science", Grade: "B" },
                              { Student: "Tom", Class: "Science", Grade: "B" },
                              { Student: "James", Class: "Chemistry", Grade: "F" },
                              { Student: "Lily", Class: "Chemistry", Grade: "A" },
                              { Student: "Bob", Class: "Chemistry", Grade: "B" },
                              { Student: "Tom", Class: "Chemistry", Grade: "A" } ];

        var classes = [];

        // Returns an array that maintains the grade counts for each class based on the grades array.
        var getGradeCounts = function (studentData, gradeCounts) {
            if (gradeCounts == null) {
                gradeCounts = [];
            }
            for (var i = 0; i < grades.length; i++) {
                if (gradeCounts[i] == undefined) {
                    gradeCounts[i] = 0;
                }
                if (grades[i].name === studentData.Grade) {
                    gradeCounts[i]++;
                }
            }
            return gradeCounts;
        }

        // Iterates over each student grade and maintains an object of the grade counts by class, and an unique array of classes used for the chart category.
        var classGradeCounts = {};
        for (var student in studentGrades) {
            var studentClass = studentGrades[student].Class;
            if (classGradeCounts[studentClass] == undefined) {
                classes.push(studentClass);
            }
            classGradeCounts[studentClass] = getGradeCounts(studentGrades[student], classGradeCounts[studentClass]);
        }

        // Now that we have the grade counts for each class, build up the chart series
        for (var i = 0; i < grades.length; i++) {
            grades[i].data = [];
            for (var classGradeCount in classGradeCounts) {
                grades[i].data.push(classGradeCounts[classGradeCount][i]);
            }
        }

        // End of stackoverflow code, the rest of this is code is used to generate a Kendo chart.

        var chartSettings = {
            seriesDefaults: { type: "column", stack: true },
            series: grades,
            categoryAxis: { categories: classes },
        };

        function createChart() {
          $("#chart").kendoChart(chartSettings);
        }

        $(document).ready(createChart);
        $(document).bind("kendo:skinChange", createChart);
      </script>
    </div>


  </body>
</html>

      

+3


source to share


1 answer


You cannot avoid loops. You can give it no loops with functions like Array.prototype.forEach

and Array.prototype.map

.

I believe the easiest way is to create maps that give you quick access to the data you need, so you don't have to constantly scan arrays and don't need helpers like underscore

or jQuery

. The advantage of these maps is that you can easily use them to create data in a different format much more easily.

You can look into the following which gives the desired result and will decide for yourself if it is easier than using regular loops.



var grades = [ { name: "A", color: "#00FF00" }, 
               { name: "B", color: "#88CC00" },
               { name: "C", color: "#AAAA00" }, 
               { name: "D", color: "#CC8800" }, 
               { name: "F", color: "#FF0000" }];

var studentGrades = [ { Student: "James", Class: "Math", Grade: "A" },
                      { Student: "Lily", Class: "Math", Grade: "B" },
                      { Student: "Bob", Class: "Math", Grade: "C" },
                      { Student: "Tom", Class: "Math", Grade: "C" },
                      { Student: "James", Class: "Science", Grade: "A" },
                      { Student: "Lily", Class: "Science", Grade: "B" },
                      { Student: "Bob", Class: "Science", Grade: "B" },
                      { Student: "Tom", Class: "Science", Grade: "B" },
                      { Student: "James", Class: "Chemistry", Grade: "F" },
                      { Student: "Lily", Class: "Chemistry", Grade: "A" },
                      { Student: "Bob", Class: "Chemistry", Grade: "B" },
                      { Student: "Tom", Class: "Chemistry", Grade: "A" } ];

var gradeMap={}, gradesByClass={}, classMap={}, classArray=[], classIndex=0;

grades.forEach(function(grade) {
    gradeMap[grade.name] = grade.color;
});
 
studentGrades.forEach(function(studentGrade){
    if (!gradesByClass[studentGrade.Class]) {
        gradesByClass[studentGrade.Class] = {};
        classMap[studentGrade.Class] = classIndex;
        classIndex++;
    }
    if(!gradesByClass[studentGrade.Class][studentGrade.Grade]) {
        gradesByClass[studentGrade.Class][studentGrade.Grade] = 0;
    }
    gradesByClass[studentGrade.Class][studentGrade.Grade]++;
});

Object.keys(classMap).forEach(function(className){
  classArray[classMap[className]] = className;
});

var finalGrades = Object.keys(gradeMap).map(function(grade){
    var obj = {
      name: grade, color: gradeMap[grade], data: []
    };
    classArray.forEach(function(className, index){
        if (gradesByClass[className] && gradesByClass[className][grade]) {
            obj.data[index] = gradesByClass[className][grade];
        } else {
            obj.data[index] = 0;
        }        
    }); 
  return obj;
});

console.log(JSON.stringify(finalGrades));
/*
[{"name":"A","color":"#00FF00","data":[1,1,2]},
 {"name":"B","color":"#88CC00","data":[1,3,1]},
 {"name":"C","color":"#AAAA00","data":[2,0,0]},
 {"name":"D","color":"#CC8800","data":[0,0,0]},
 {"name":"F","color":"#FF0000","data":[0,0,1]}]
*/
      

Run codeHide result


+3


source







All Articles