angulartypescriptchartschart.jsng2-charts

Angular 9 Chart.js stacked bar chart with multiple bars


I implemented Chart.js in my Angular 9 application. There expect chart values are not coming.

I have an API response in that I want to make barChartData as below mentioned way stackblitz Demo.

This is my API response:

let data = [
  { operatorName: 'MBC', label: 'Application', subLabel: 'Positive', count: 1 },
  { operatorName: 'MBC', label: 'Channels', subLabel: 'Negative', count: -1 },
  { operatorName: 'MBC', label: 'Customer Care', subLabel: 'Negative', count: -1 },
  { operatorName: 'MBC', label: 'Customer Care', subLabel: 'Positive', count: 1 },
  { operatorName: 'OSEN+', label: 'Application', subLabel: 'Negative', count: -1 },
  { operatorName: 'OSEN+', label: 'Application', subLabel: 'Positive', count: 1 },
  { operatorName: 'OSEN+', label: 'Channels', subLabel: 'Positive', count: 1},
  { operatorName: 'OSEN+', label: 'Customer Care', subLabel: 'Positive', count: 1 }
];

I want to set barChartData with the API response.

Expected Data set:

this.barChartLabels = ['Application', 'Customer Care', 'Package'];
this.barChartData = [
  {
    label: 'OSEN+ Passtive',
    type: 'bar',
    stack: 'OSEN+',
    data: [30, 31, 23],
  },
  {
    label: 'OSEN+ Negative',
    type: 'bar',
    stack: 'OSEN+',
    data: [-15, -16],
  },
  {
    label: 'MBC Passtive',
    type: 'bar',
    stack: 'MBC',
    data: [20, 21],
  },
  {
    label: 'MBC Negative',
    type: 'bar',
    stack: 'MBC',
    data: [-10, -11],
  },
];

I tried to implement the logic here:

let labels = [...new Set(data.map((x) => x.label))];
let operators = [...new Set(data.map((x) => x.operatorName))];
let subLabels = [...new Set(data.map((x) => x.subLabel))];
let subLabelDatasets = subLabels.map((x) => {
  let datasets = [];
  let opratorlabes = [];
  for (let label of labels) {
    datasets.push(
      data.find((y) => y.label == label && y.subLabel == x)?.count || 0
    );
  }

  //console.log(data)
  return {
    label: opratorlabes,
    data: datasets,
  };
});

Expected result: enter image description here


Solution

  • Compare to the previous answer I have written, the concept is similar.

    For the subLabels, since you need to group the data by operatorName and subLabel, you need to distinct the group for the objects containing these 2 properties.

    While in the dataset of each object in subLabelDatasets, you can find the value by getting the value based on label, operatorName, and subLabel from the data array.

    let labels = [...new Set(data.map((x) => x.label))];
    let subLabels = data.reduce((acc, cur: any) => {
      if (
        acc.findIndex(
          (x) =>
            x.operatorName == cur.operatorName && x.subLabel == cur.subLabel
        ) == -1
      )
        acc.push({ operatorName: cur.operatorName, subLabel: cur.subLabel });
    
      return acc;
    }, [] as { operatorName: string; subLabel: string }[]);
    
    let subLabelDatasets = subLabels.map((x) => {
      let datasets = [];
    
      for (let label of labels) {
        datasets.push(
          data.find(
            (y) =>
              y.label == label &&
              y.subLabel == x.subLabel &&
              y.operatorName == x.operatorName
          )?.count || 0
        );
      }
    
      return {
        label: x.operatorName + ' ' + x.subLabel,
        data: datasets,
        stack: x.operatorName,
        type: 'bar',
      };
    });
    
    this.barChartLabels = labels;
    this.barChartData = subLabelDatasets;
    

    Demo @ StackBlitz