stig
stig

Reputation: 1220

Chart.js Bar Chart - grouping and mapping data

I am trying to use Chart.js Bar Chart to display a set of data. My data is weekly based so to my method I send the year and week and get the data back in 3 columns; Product, Area and Amount.

What I want is to have to Products horizontaly and in each Product I want different bars for each Area and offcourse the Amount verticaly. (Bonus: If an Area nothing in that product it should not be shown in that particular Product)

The problem is that the number of Products and the number of Areas can vary from each week. And I can't seem to find a good way to loop through the data and create the datasets the way chart.js wants.

Also tried using Underscore.js to group it but the fact that the each Area doesn't always have an amount for a spesific product seems to be causing some issues.

So I guess you have to loop through the data and map that data to another predefined array for each Area so it can match this structure somehow??

Also open for other Chart plugins, but really liked how Chart.js animates the data. And if I get this working I can probably figgure out an update method for when you change week.

To get the labels i can f.ex do this:

$.ajax({
    ....
    success: function (d) {
          var a = _.groupBy(d.data, function (d) { return d.Product });
          var labels = [];
          $.each(a, function (i, value) {
             labels.push(i);
          });
    }
});

Upvotes: 2

Views: 7138

Answers (1)

potatopeelings
potatopeelings

Reputation: 41075

With data in this format

var myJSONData = [
    {
        Product: 'P1',
        Area: 'A1',
        Value: 12
    },
    ...
]

You can use this function to convert it into the format Chart.js requires

var data = {
    labels: [],
    datasets: []
}

var colors = ['Red','Blue','Green', ...]  // add as many colors as there will be areas (maximum)

myJSONData.forEach(function (e) {
    // create labels
    var labelIndex = data.labels.indexOf(e.Product)
    if (labelIndex === -1) {
        labelIndex = data.labels.length;
        data.labels.push(e.Product);
        // dummy entries for each dataset for the label
        data.datasets.forEach(function (dataset) {
            dataset.data.push(0)
        })
    }

    // get the area dataset
    var area = data.datasets.filter(function(area){
        return (area.label === e.Area);
    })[0]
    // otherwise create it
    if (area === undefined) {
        area = {
            label: e.Area,
            // create a dummy array with an entry for each of the existing labels
            data: data.labels.map(function () {
                return 0;
            }),
            fillColor: colors[data.datasets.length]
        };
        data.datasets.push(area)
    }

    // set the value
    area.data[labelIndex] = e.Value;
})

and use that to display the chart.


Fiddle - http://jsfiddle.net/jt4Lqkn3/


(Bonus: If an Area nothing in that product it should not be shown in that particular Product)

You can't change any configuration to do this - there will be a space left for each series.

However you might want to set the strokeColor to a transparent value (e.g. strokeColor: "rgba(0, 0, 0, 0)", just below the fillColor line) and set the barStrokeWidth option to 0, so that 0 values don't show up at all on the chart (otherwise there will be thin line shown)

new Chart(ctx).Bar(data, {
    barStrokeWidth: 0,
});

Upvotes: 2

Related Questions