1

I have a stacked bar but I want to order the bars based on the summed value of my two datasets. Does any one have any idea how to do this using chart.js functionality?

See here

data: {
        labels: stores,
        datasets: [
        {
            label: 'Rev',
            data: dataPack1,
                        backgroundColor: "rgba(55, 160, 225, 0.7)",
                        hoverBackgroundColor: "rgba(55, 160, 225, 0.7)",
                        hoverBorderWidth: 2,
                        hoverBorderColor: 'lightgrey'
        },
        {
            label: 'Tax1',
            data: dataPack2,
                        backgroundColor: "rgba(225, 58, 55, 0.7)",
                        hoverBackgroundColor: "rgba(225, 58, 55, 0.7)",
                        hoverBorderWidth: 2,
                        hoverBorderColor: 'lightgrey'
        },
        ]
    },
danday74
  • 52,471
  • 49
  • 232
  • 283
Thanos
  • 142
  • 1
  • 8
  • what do you mean - **order the bars based on summed value of two datasets**? could you elaborate? – ɢʀᴜɴᴛ Aug 22 '17 at 14:45
  • sure, what I would like to have is the chart in the link https://jsfiddle.net/tuw3sdho/3/ had to change the data var dataPack1 = [60000, 59000,56000,55000, 55000,35000,26000, 22000,21000]; var dataPack2 = [4100,4000,2070,2030,1060,1400,1300, 1200, 1000 ]; – Thanos Aug 22 '17 at 14:49
  • @Thanos put answers to comments in the body of the question, please. –  Aug 22 '17 at 15:00

2 Answers2

1

This could be achieved more efficiently and effectively using the following chart plugin :

Chart.plugins.register({
   datasets: [],
   getData(labels, datasets) {
      const sum = [];
      for (i = 0; i < datasets[0].length; i++) {
         sum.push({
            label: labels[i],
            data: datasets.map(e => e[i]),
            get sum() { // ES6 - getter
               return this.data.reduce((a, b) => a + b);
            }
         });
      }
      return sum;
   },
   beforeInit(chart) {
      chart.data.datasets.forEach((dataset, datasetIndex) => {
         this.datasets.push(dataset.data);
      });
      const data_store = this.getData(chart.data.labels, this.datasets),
            sorted = data_store.map(e => e.sum).sort((a, b) => b - a);
      sorted.forEach((n, ni) => {
         data_store.forEach(d => {
            if (d.sum === n) {
               chart.data.labels[ni] = d.label;
               d.data.forEach((v, vi) => {
                  chart.data.datasets[vi].data[ni] = v;
               });
            }
         });
      });
   }
});

* add this plugin at the beginning of your script.

ᴡᴏʀᴋɪɴɢ ᴇxᴀᴍᴘʟᴇ

Chart.plugins.register({
   datasets: [],
   getData(labels, datasets) {
      const sum = [];
      for (i = 0; i < datasets[0].length; i++) {
         sum.push({
            label: labels[i],
            data: datasets.map(e => e[i]),
            get sum() { // ES6 - getter
               return this.data.reduce((a, b) => a + b);
            }
         });
      }
      return sum;
   },
   beforeInit(chart) {
      chart.data.datasets.forEach((dataset, datasetIndex) => {
         this.datasets.push(dataset.data);
      });
      const data_store = this.getData(chart.data.labels, this.datasets),
          sorted = data_store.map(e => e.sum).sort((a, b) => b - a);
      sorted.forEach((n, ni) => {
         data_store.forEach(d => {
            if (d.sum === n) {
               chart.data.labels[ni] = d.label;
               d.data.forEach((v, vi) => {
                  chart.data.datasets[vi].data[ni] = v;
               });
            }
         });
      });
   }
});

var numberWithCommas = function(x) {
   return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};

var dataPack1 = [21000, 22000, 26000, 35000, 55000, 55000, 56000, 59000, 60000];
var dataPack2 = [1000, 1200, 1300, 140000, 1060, 2030, 2070, 4000, 4100];
var stores = ["A", "B", "C", "D", "E", "F", "G", "H", "I"];

var bar_ctx = document.getElementById('bar-chart');
var bar_chart = new Chart(bar_ctx, {
   type: 'bar',
   data: {
      labels: stores,
      datasets: [{
         label: 'Rev',
         data: dataPack1,
         backgroundColor: "rgba(55, 160, 225, 0.7)",
         hoverBackgroundColor: "rgba(55, 160, 225, 0.7)",
         hoverBorderWidth: 2,
         hoverBorderColor: 'lightgrey'
      }, {
         label: 'Tax1',
         data: dataPack2,
         backgroundColor: "rgba(225, 58, 55, 0.7)",
         hoverBackgroundColor: "rgba(225, 58, 55, 0.7)",
         hoverBorderWidth: 2,
         hoverBorderColor: 'lightgrey'
      }]
   },
   options: {
      animation: {
         duration: 10,
      },
      tooltips: {
         mode: 'label',
         callbacks: {
            label: function(tooltipItem, data) {
               return data.datasets[tooltipItem.datasetIndex].label + ": " + numberWithCommas(tooltipItem.yLabel);
            }
         }
      },
      scales: {
         xAxes: [{
            stacked: true,
            gridLines: {
               display: false
            },
         }],
         yAxes: [{
            stacked: true,
            ticks: {
               callback: function(value) {
                  return numberWithCommas(value);
               },
            },
         }],
      },
      legend: {
         display: true
      }
   }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.min.js"></script>
<canvas id="bar-chart" width="600" height="350"></canvas>
ɢʀᴜɴᴛ
  • 32,025
  • 15
  • 116
  • 110
  • This would not change the X-axis. In this case the highest y-value coresponds to x-axis value "I", if i use sort in data then the relationship is lost. For example before sort for x-value "I" Y-value=[60000, 4100 ], after sort, for x-value "I" the Y-value is = [21000,1000]. check the x-axis in this https://jsfiddle.net/tuw3sdho/3/ had to change stores from var stores = ["A", "B", "C", "D", "E", "F","G", "H", "I"]; to var stores = ["I","H","G","F","E", "D", "C", "B", "A"]; – Thanos Aug 22 '17 at 15:08
  • hmm, yes that is not working always... i changed the values and.... https://jsfiddle.net/zhocr17t/17/ var dataPack2 = [1000, 1200, 1300, 140000, 1060, 2030, 2070, 4000, 4100].sort((a, b) => b - a);; – Thanos Aug 22 '17 at 15:23
  • the function that sorts the values is perfect but it does not sort respectively the labels. In the code snippet above the label "I" does not have the highest total sum, the label "E" has it. So i took the liberty to make a small adittion re-ordering also the labels. check here: https://jsfiddle.net/m673nujw/2/ – Thanos Aug 23 '17 at 07:34
  • COOL!! However, I updated the answer with a plugin, which can be used to do this job more efficiently (especially when there are multiple datasets). – ɢʀᴜɴᴛ Aug 23 '17 at 12:52
1

If you have a scenario where multiple stacked bars have the same sum total, the other answer will duplicate the labels.

This will not duplicate the labels:

        Chart.plugins.register({                                                                                                                                                             
        datasets: [],                                                                                                                                                                    
        getData(labels, datasets) {                                                                                                                                                      
            const sum = [];                                                                                                                                                              
            for (i = 0; i < datasets[0].length; i++) {                                                                                                                                   
                sum.push({                                                                                                                                                               
                    label: labels[i],                                                                                                                                                    
                    data: datasets.map(e => e[i]),                                                                                                                                       
                        get sum() { // ES6 - getter                                                                                                                                      
                            return this.data.reduce((a, b) => a + b);                                                                                                                    
                        }                                                                                                                                                                
                });                                                                                                                                                                      
            }                                                                                                                                                                            
            return sum;                                                                                                                                                                  
        },                                                                                                                                                                               
        beforeInit(chart) {                                                                                                                                                              
            chart.data.datasets.forEach((dataset, datasetIndex) => {                                                                                                                     
                this.datasets.push(dataset.data);                                                                                                                                        
            });                                                                                                                                                                          
            const data_store = this.getData(chart.data.labels, this.datasets).sort((a,b) => b.sum - a.sum);                                                                              
                                                                                                                                                                                         
            data_store.forEach((d,i) => {                                                                                                                                                
                chart.data.labels[i] = d.label;                                                                                                                                          
                d.data.forEach((v, vi) => {                                                                                                                                              
                    chart.data.datasets[vi].data[i] = v;                                                                                                                                 
                });                                                                                                                                                                      
            });                                                                                                                                                                          
        }                                                                                                                                                                                
    });   
Fareesh Vijayarangam
  • 5,037
  • 4
  • 23
  • 18