0

I'm trying to create a line chart that can have up to two points in one category in chartjs. In the example, the labels and data are each passed as arrays, so each label can only have one point. I would like to have up to two points.

I tried to put extra entries in the data, but the extra entry did not do anything.

    data: {
        datasets: [{
            data: [10, 20, 30, 40, 50, 60, 70] // Only the first 6 entries are shown.
        }],
        labels: ['January', 'February', 'March', 'April', 'May', 'June']
    },

2 Answers 2

0

I don't see a simple solution to this. Each dataset data should be an array of data points, while the labels array should provide x axis values for those data points if the x axis is of type category, which is the default for a chart of type line.

Let's say that our data looks like this:

const data = {
    datasets: [{
        data: [10, 20, 30, 40, 50, 60, 70, 80]
    }],
    labels: ['January', 'February', 'February', 'March', 'April', 'May', 'June', 'June'],
};

by which we are defining the fact that two categories, the one for February and the one for June have two points while the others one point; other possible conventions might be used, if one wants to avoid duplication of text prone to typos.

To have a better control of the point positions and of the labels of the x axis you may employ a (possibly hidden) linear axis, together with setting relevant x values in the datasets data.

There are two possibilities: the first, if you want the categories to have the same size, then the points in the categories with two points will be closer together.

In this case one may use a secondary hidden linear x axis to position the points, while showing a uniform category x axis with the labels without repetitions:

const data = {
    datasets: [{
        data: [10, 20, 30, 40, 50, 60, 70, 80]
    }],
    labels: ['January', 'February', 'February', 'March', 'April', 'May', 'June', 'June'],
};

let xi = 0;
data.datasets.forEach(dataset => {
    dataset.data = dataset.data.map((y, i) => {
        let x = xi + 0.5;
        if(data.labels[i] === data.labels[i-1]){
            x += 1/6;
        }
        else if(data.labels[i] === data.labels[i+1]){
            x -= 1/6;
            xi -= 1;
        }
        xi += 1;
        return ({x, y});
    })
});

const options = {
    maintainAspectRatio: false,
    plugins: {
        tooltip:{
            callbacks: {
                title: ([{dataIndex}]) => data.labels[dataIndex]
            }
        },
        legend: {
            display: false,
        }
    },
    scales: {
        x: {
            display: false,
            type: 'linear',
        },
        x2:{
            type: 'category',
            offset: true,
            labels: [... new Set(data.labels)],
            grid: {
                color: 'rgba(0,0,0,0.4)',
                offset: true
            }
        },
        y: {
            beginAtZero: true,
        },
    },
};

new Chart('myChart', {type: 'line', options, data});
<canvas style="height: 160px" id="myChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

The second case, if you want to keep points equidistant and have the categories with variable widths, such that a category with two points will be twice the size of a one point one.

In this case, we should keep only the linear x axis visible, but modify its labels to correspond to the original data.labels. A second, similar, linear x axis might be the solution to show grid lines separating the categories:

const data = {
    datasets: [{
        data: [10, 20, 30, 40, 50, 60, 70, 80]
    }],
    labels: ['January', 'February', 'February', 'March', 'April', 'May', 'June', 'June'],
};

data.datasets.forEach(dataset => {
    dataset.data = dataset.data.map((y, i) => ({x: i+0.5, y}));
});

const options = {
    maintainAspectRatio: false,
    plugins: {
        tooltip:{
            callbacks: {
                title: ([{dataIndex}]) => data.labels[dataIndex]
            }
        },
        legend: {
            display: false,
        }
    },
    scales: {
        x: {
            type: 'linear',
            min: 0,
            max: data.labels.length,
            ticks: {
                stepSize: 0.5,
                callback: (value) => {
                    const idx = Math.floor(value),
                        label = data.labels[idx];
                    if(Math.abs(Math.round(value) - value) < 1e-6){
                        if(label === data.labels[idx - 1]){
                            return label;
                        }
                        else{
                            return null;
                        }
                    }
                    else{
                        if(label === data.labels[idx - 1] || label === data.labels[idx + 1]){
                            return null;
                        }
                        return label;
                    }
                }
            }
        },
        x2:{
            type: "linear",
            min: 0,
            max: data.labels.length,
            ticks: {
                display: false,
                stepSize: 0.5,
                callback(value, ...args){
                    if(value === this.max || value === this.min){
                        return '';
                    }

                    if(Math.abs(Math.round(value) - value) < 1e-6){
                        const idx = Math.floor(value),
                            label = data.labels[idx];
                        if(label === data.labels[idx - 1]){
                            return null;
                        }
                        else{
                            return '';
                        }
                    }
                    else{
                        return null;
                    }
                }
            },
            grid: {
                color: 'rgba(0,0,0,0.4)',
                drawTicks: false
                //offset: true
            }
        },
        y: {
            beginAtZero: true,
            //display: false,
        },
    },
};

new Chart('myChart', {type: 'line', options, data});
<canvas style="height: 160px" id="myChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

0

short answer

actually data is an array of datasets so u can exactly add one more dataset object in array

labels,
datasets: [  //  a r r a y
  {
    fill: true,
    label,
    data,
    backgroundColor,
    borderColor,
  },
  {
       A N O T H  E R  D A T A S E T
   }
],

Not the answer you're looking for? Browse other questions tagged or ask your own question.