Search code examples
javascriptjqueryjsongroupingsummary

Summarize and Group JSON object in Jquery


I am having problems summarizing and grouping my object in Jquery as I would like to group by country and then have a grand total based on subperiods 4,5,and 6 for values within col1. The col1, col2, col3 are dynamic, but using this is an example.

What I need is to get the total by country which is stored in the country variable, grand total per subperiod stored in the columns variable, and the total count stored in the averages variable.

Here is my code in jsfiddle:

http://jsfiddle.net/8vVK7/8/

My object:

var g_jsonData =

[
{
    "Country": "Spain",
    "Year": "2000",
    "Subperiod": "4",
    "col1": "75",
    "col2": "500",
    "col3": "200"
},
{
    "Country": "Spain",
    "Year": "2001",
    "Subperiod": "4",
    "col1": "50",
    "col2": "500",
    "col3": "300"
},
{
    "Country": "Spain",
    "Year": "2002",
    "Subperiod": "4",      
    "col1": "50",
    "col2": "200",
    "col3": "300"
},
{
    "Country": "Spain",
    "Year": "2003",
    "Subperiod": "4",      
    "col1": "",
    "col2": "200",
    "col3": "300"
},
{
    "Country": "Spain",
    "Year": "2005",
    "Subperiod": "5",
    "col1": "125",
    "col2": "500",
    "col3": "300"
},
{
    "Country": "Spain",
    "Year": "2012",
    "Subperiod": "6",
    "col1": "100.75",
    "col2": "500",
    "col3": "200"
},
{
    "Country": "Spain",
    "Year": "2013",
    "Subperiod": "6",
    "col1": "200",
    "col2": "500",
    "col3": "300"
},
{
    "Country": "France",
    "Year": "2000",
    "Subperiod": "4",    
    "col1": "100",
    "col2": "100",
    "col3": "300"
},
{
    "Country": "France",
    "Year": "2001",
    "Subperiod": "4",      
    "col1": "100",
    "col2": "100",
    "col3": "300"
},
{
    "Country": "France",
    "Year": "2002",
    "Subperiod": "4",      
    "col1": "100",
    "col2": "200",
    "col3": "300"
},
{
    "Country": "France",
    "Year": "2005",
    "Subperiod": "5",      
    "col1": "100",
    "col2": "200",
    "col3": "300"
},
{
    "Country": "France",
    "Year": "2012",
    "Subperiod": "6",      
    "col1": "100",
    "col2": "100",
    "col3": "300"
},
{
    "Country": "France",
    "Year": "2013",
    "Subperiod": "6",      
    "col1": "100",
    "col2": "100",
    "col3": "300"
}];

Here is the end result which I would like to get and this is based on summarizing col1 per subperiod per country.

countries =    
{
   "Spain":{
      "4":175,
      "5":125,
      "6":300.75
  },    
   "France":{
      "4":300,
      "5":100,
      "6":200,
  }
}

Grand total for the subperiods 4,5,6 should show as:

columns = 
{
    "4":475,
    "5":225,
    "6":500.75
} 

I would also like to get the total count per country, per subperiod:

averages = 
{
   "Spain":{
      "4":3,
      "5":1,
      "6":2
  },    
   "France":{
      "4":3,
      "5":1,
      "6":2
  }
}   

I would like to try to stick to my code as close as possible. Thanks, appreciate the help.


Solution

  • This should do the trick:

    g_jsonData.reduce(function(out, curr){
        // Make sure the current country exists on the output object.
        // Then add the current object's col1 to the current country's subperiod,
        //   checking to see if the current subperiod exists. Also, cast the `col1` to int `(+curr.col1)`
        out.countries[curr.Country] = out.countries[curr.Country] || {};
        out.countries[curr.Country][curr.Subperiod] = (out.countries[curr.Country][curr.Subperiod] || 0) + (+curr.col1);
    
        // And add it to the total, too.
        out.columns[curr.Subperiod] = (out.columns[curr.Subperiod] || 0) + (+curr.col1);
    
        // Count occurences
        out.count[curr.Country] = out.count[curr.Country] || {};
        out.count[curr.Country][curr.Subperiod] = (out.count[curr.Country][curr.Subperiod] || 0) + 1;
        return out;
    },
    {'columns':{},'countries':{},'count':{}}); // second parameter to `reduce` is a default object, in this case: `{'columns':{},'countries':{},'count':{}}}`
    

    Returns:

    {
        "columns": {
            "4": 475,
            "5": 225,
            "6": 500.75
        },
        "countries": {
            "Spain": {
                "4": 175,
                "5": 125,
                "6": 300.75
            },
            "France": {
                "4": 300,
                "5": 100,
                "6": 200
            }
        },
        "count": {
            "Spain": {
                "4": 3,
                "5": 1,
                "6": 2
            },
            "France": {
                "4": 3,
                "5": 1,
                "6": 2
            }
        }
    }
    

    Please note this doesn't change g_jsonData, it only returns the new object.
    To use the new object, you have to assign it to a variable, of course:

    var output = g_jsonData.reduce(func....);
    

    You could make the code a little shorter (/easier to look at) like this:

    g_jsonData.reduce(function(out, curr){
        var c = curr.Country,
            s = curr.Subperiod,
            v = +curr.col1; //value cast to int already
        out.countries[c]    =  out.countries[c]    || {};
        out.countries[c][s] = (out.countries[c][s] || 0 ) + v;
        out.count[c]        =  out.count[c]        || {};
        out.count[c][s]     = (out.count[c][s]     || 0 ) + 1;
        out.columns[s]      = (out.columns[s]      || 0 ) + v;
        return out;
    },
    {'columns':{},'countries':{},'count':{}});
    

    Now it's relatively easy to make col1 a variable name.
    Replace:

    v = +curr.col1;
    

    With:

    v = +curr['col1'];
    

    Or:

    v = +curr[someVariable];