Meet
Meet

Reputation: 243

Chart Js Data transformation using Javascript

I need help in transforming data. I need to plot a stacked graph. The data which I am getting from the backend is not in chart js form. I need to calculate the percentage of the count of keywords by the same verticalName example (in my case Attrition in BCM is 56.52). Could you please guide me on how to tackle this problem?

const d = [
  {
    name: 'Attrition',
    keywordId: 1,
    verticalId: 1,
    verticalName: 'BCM',
    countOfKeywords: 20,
  },
  {
    name: 'Others',
    keywordId: 2,
    verticalId: 1,
    verticalName: 'BCM',
    countOfKeywords: 26,
  },
  {
    name: 'Others',
    keywordId: 2,
    verticalId: 2,
    verticalName: 'CGLR',
    countOfKeywords: 24,
  },
];

What I tried?

const amp = d.map((a) => {
  const vertName = a.verticalName;
  if (obj && vertName in obj) {
    obj[a.verticalName].sum += a.countOfKeywords;
    obj[a.verticalName].keywords.push({ name: a.name, countOfKeyword: a.countOfKeywords });
  } else {
    obj[a.verticalName] = {};
    obj[a.verticalName].keywords = [];
    obj[a.verticalName].keywords.push({ name: a.name, countOfKeyword: a.countOfKeywords });
    obj[a.verticalName].sum = a.countOfKeywords;
  }
});

for (let i in obj) {
  obj[i].keywords.forEach(e => {
    e.pert = (e.countOfKeyword / obj[i].sum) * 100;
  })
}

Output from my code -->

{
  "BCM": {
    "keywords": [
      {
        "name": "Attrition",
        "countOfKeyword": 20,
        "pert": 43.47826086956522
      },
      {
        "name": "Others",
        "countOfKeyword": 26,
        "pert": 56.52173913043478
      }
    ],
    "sum": 46
  },
  "CGLR": {
    "keywords": [
      {
        "name": "Others",
        "countOfKeyword": 24,
        "pert": 100
      }
    ],
    "sum": 24
  }
}

End result what chart js required?

here dataset array consists of objects which includes another data array.

In labels, array 0 index represents BCM same rule applies to data property in datasets. if any name (keyword like attrition or others) are missing for a verticalName. We need to add 0 value in data array in a specific index.

example Attrition is not available in CGLR vertical so we need to add 0 in that index.

{
    labels: ['BCM', 'CGLR'],
      datasets: [
        {
          label: 'Attrition',
          data: [43.47, 0]
        },
        {
          label: 'Others',
          data: [ 56.52, 100]
        }
      ]
}

thanks, meet

Upvotes: 2

Views: 639

Answers (1)

Ahmed ElMetwally
Ahmed ElMetwally

Reputation: 2383

My solution will create chartjs data with two steps first step I will create grouping data and template data.

  • Grouping data which contain each name and verticalName with his countOfKeywords.
  • Template data which contain chartjs data with 0 percentages

Second step I will get data from grouping and put it in the template.


Input

const array = [
    {
        name: 'Attrition',
        verticalName: 'BCM',
        countOfKeywords: 20,
    },
    {
        name: 'Others',
        verticalName: 'BCM',
        countOfKeywords: 26,
    },
    {
        name: 'Others',
        verticalName: 'CGLR',
        countOfKeywords: 24,
    },
    {
        name: 'Attrition',
        verticalName: 'NEW',
        countOfKeywords: 20,
    },
    {
        name: 'Others',
        verticalName: 'NEW',
        countOfKeywords: 1,
    },
];

After Grouping

{
  "BCM": {
    "sum": 46,
    "keywords": {
      "Attrition": 20,
      "Others": 26
    }
  },
  "CGLR": {
    "sum": 24,
    "keywords": {
      "Others": 24
    }
  },
  "NEW": {
    "sum": 21,
    "keywords": {
      "Attrition": 20,
      "Others": 1
    }
  }
}

Template

{
  "labels": [
    "BCM",
    "CGLR",
    "NEW"
  ],
  "datasets": [
    {
      "label": "Attrition",
      "data": [
        0,
        0,
        0
      ]
    },
    {
      "label": "Others",
      "data": [
        0,
        0,
        0
      ]
    }
  ]
}

Output

{
  "labels": [
    "BCM",
    "CGLR",
    "NEW"
  ],
  "datasets": [
    {
      "label": "Attrition",
      "data": [
        43.47826086956522,
        0,
        95.23809523809523
      ]
    },
    {
      "label": "Others",
      "data": [
        56.52173913043478,
        100,
        4.761904761904762
      ]
    }
  ]
}

Full code

// input
const array = [
    {
        name: 'Attrition',
        verticalName: 'BCM',
        countOfKeywords: 20,
    },
    {
        name: 'Others',
        verticalName: 'BCM',
        countOfKeywords: 26,
    },
    {
        name: 'Others',
        verticalName: 'CGLR',
        countOfKeywords: 24,
    },
    {
        name: 'Attrition',
        verticalName: 'NEW',
        countOfKeywords: 20,
    },
    {
        name: 'Others',
        verticalName: 'NEW',
        countOfKeywords: 1,
    },
];

// declare variables
let uniqueNames = new Set();
const grouping = {};
const template = {
    labels: [],
    datasets: []
}

// create grouping object and create template labels 
for (let i = 0; i < array.length; i += 1) {
    const element = array[i];

    if (grouping[element.verticalName] === undefined) {
        template.labels.push(element.verticalName);
        grouping[element.verticalName] = { sum: 0, keywords: {} };
    };

    grouping[element.verticalName].sum += element.countOfKeywords;
    grouping[element.verticalName].keywords[element.name] = grouping[element.verticalName].keywords[element.name] ? grouping[element.verticalName].keywords[element.name] + element.countOfKeywords : element.countOfKeywords

    uniqueNames.add(element.name);
}

// uniqueNames Set to Array   
uniqueNames = [...uniqueNames];

// create template datasets with 0 percentage
// I did it after first loop because I want to makesure the template.labels.length completed
template.datasets = uniqueNames.map(label => ({ label, data: new Array(template.labels.length).fill(0) }));

// target for now create grouping & create template with labels and 0 percentages
// console.log(JSON.stringify(grouping, null, 2));
// console.log(JSON.stringify(template, null, 2));

// calculate the percentages
for (let i = 0; i < template.datasets.length; i++) {
    const uniqueName = uniqueNames[i];

    for (let j = 0; j < template.labels.length; j++) {
        const label = template.labels[j];
        if (grouping[label].keywords[uniqueName] === undefined) {
            template.datasets[i].data[j] = 0;
        } else {
            template.datasets[i].data[j] = grouping[label].keywords[uniqueName] / grouping[label].sum * 100;
        }
    }
}

// output
console.log(JSON.stringify(template, null, 2));


UPDATE

More optimized code by skip add datasets to template before second loop

// input
const array = [
    {
        name: 'Attrition',
        verticalName: 'BCM',
        countOfKeywords: 20,
    },
    {
        name: 'Others',
        verticalName: 'BCM',
        countOfKeywords: 26,
    },
    {
        name: 'Others',
        verticalName: 'CGLR',
        countOfKeywords: 24,
    },
    {
        name: 'Attrition',
        verticalName: 'NEW',
        countOfKeywords: 20,
    },
    {
        name: 'Others',
        verticalName: 'NEW',
        countOfKeywords: 1,
    },
];

// declare variables
let uniqueNames = new Set();
const grouping = {};
const template = {
    labels: [],
    datasets: []
}

// create grouping object and create template labels 
for (let i = 0; i < array.length; i += 1) {
    const element = array[i];

    if (grouping[element.verticalName] === undefined) {
        template.labels.push(element.verticalName);
        grouping[element.verticalName] = { sum: 0, keywords: {} };
    };

    grouping[element.verticalName].sum += element.countOfKeywords;
    grouping[element.verticalName].keywords[element.name] = grouping[element.verticalName].keywords[element.name] ? grouping[element.verticalName].keywords[element.name] + element.countOfKeywords : element.countOfKeywords

    uniqueNames.add(element.name);
}

// uniqueNames Set to Array   
uniqueNames = [...uniqueNames];

// target for now create grouping & create template with labels and without datasets 
// console.log(JSON.stringify(grouping, null, 2));
// console.log(JSON.stringify(template, null, 2));
 
// add datasets
// calculate the percentages
for (let i = 0; i < uniqueNames.length; i++) {
    const uniqueName = uniqueNames[i];

    template.datasets.push({ label: uniqueName, data: [] })

    for (let j = 0; j < template.labels.length; j++) {
        const label = template.labels[j];
        if (grouping[label].keywords[uniqueName] === undefined) {
            template.datasets[i].data[j] = 0;
        } else {
            template.datasets[i].data[j] = grouping[label].keywords[uniqueName] / grouping[label].sum * 100;
        }
    }
}

// output
console.log(JSON.stringify(template, null, 2));

Upvotes: 1

Related Questions