Stacking series with mismatched categories/dates

Normally, stacking series on an XY chart requires that they have same number of data items, and that their categories or dates match. This tutorial will introduce a custom function that can merge data from a number of sources so that multiple series can reuse it for correct stacking.

The problem

For correct stacking of multiple series, their data must match both number of data items and their respective categories or timestamps.

For example, let's take these two data sets:

const data1 = [{
  category: "2022",
  value: 2.6
}, {
  category: "2023",
  value: 2.8
}];

const data2 = [{
  category: "2021",
  value: 2.5
}, {
  category: "2022",
  value: 2.7
}, {
  category: "2023",
  value: 2.9
}];
var data1 = [{
  category: "2022",
  value: 2.6
}, {
  category: "2023",
  value: 2.8
}];

var data2 = [{
  category: "2021",
  value: 2.5
}, {
  category: "2022",
  value: 2.7
}, {
  category: "2023",
  value: 2.9
}];

If we would use these arrays for two stacked series, the resulting chart would be completely off.

The solution

In order to solve this we will create a function that merges data from multiple series into a single array, which all series can universally use.

The target is to create a single array that would look like this:

[{
  category: "2021",
  value2: 2.5
}, {
  category: "2022",
  value1: 2.6,
  value2: 2.7
}, {
  category: "2023",
  value1: 2.8
  value2: 2.9
}]

Here's the function that could do that:

mergeData(data1, "category", "value", "value1");
mergeData(data2, "category", "value", "value2");


function mergeData(data, positionField, sourceValueField, targetValueField) {
  am5.array.each(data, function(item) {
    var found = false;
    am5.array.each(combinedData, function(item2) {
      if(item2[positionField] == item[positionField]) {
        found = true;
        item2[targetValueField] = item[sourceValueField];
      }
    });
    if (!found) {
      var newItem = {};
      newItem[positionField] = item[positionField];
      newItem[targetValueField] = item[sourceValueField];
      combinedData.push(newItem);
    }
  });
  
  combinedData.sort(function(a, b) {
    if (a[positionField] > b[positionField]) return 1;
    if (a[positionField] < b[positionField]) return -1;
    return 0;
  });
}
mergeData(data1, "category", "value", "value1");
mergeData(data2, "category", "value", "value2");


function mergeData(data, positionField, sourceValueField, targetValueField) {
  am5.array.each(data, function(item) {
    var found = false;
    am5.array.each(combinedData, function(item2) {
      if(item2[positionField] == item[positionField]) {
        found = true;
        item2[targetValueField] = item[sourceValueField];
      }
    });
    if (!found) {
      var newItem = {};
      newItem[positionField] = item[positionField];
      newItem[targetValueField] = item[sourceValueField];
      combinedData.push(newItem);
    }
  });
  
  combinedData.sort(function(a, b) {
    if (a[positionField] > b[positionField]) return 1;
    if (a[positionField] < b[positionField]) return -1;
    return 0;
  });
}

Demos

With category axis

See the Pen
Stacking series with mismatched categories
by amCharts team (@amcharts)
on CodePen.0

With date axis

See the Pen
Stacking series with mismatched data item counts
by amCharts team (@amcharts)
on CodePen.0

Posted in Uncategorized