Javascript HighChart having multiple name & data in Series and json object - javascript

I have a JSON object which is retrieved from MySQL using PHP.It is as follows:
{
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, null, null]
}
The content can vary as it totally depends on data retrieved from database at any point of time.Now in Javascript I 'm plotting HighCharts, and need to use the data retrieved from JSON object, i.e. I' m having series:
[{
name: 'A',
data: [1, 2, 3]
}, {
name: 'B',
data: [4, 5, 6]
}, {
name: 'C',
data: [7, , ]
}]
Here I have typed array data, but it has to be dynamic, as I'll not be knowing the contents of the array. My expected output is as follows:
[{
name: ' A ',
data: Array[' A ']
// should give the data of [1,2,3]
}, {
name: ' B ',
data: Array[' B ']
}, {
name: ' C ',
data: Array[' C ']
}]
Kindly suggest how to proceed?

You can use JSON_NUMERIC_CHECK http://php.net/manual/en/json.constants.php to form proper associative array in php.
For current scenario you can update you json data dynamically
{
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, null, null]
}
to required form
[{
"name": "A",
"data": [1, 2, 3]
}, {
"name": "B",
"data": [4, 5, 6]
}, {
"name": "C",
"data": [7, 0, 0]
}]
by using code below
var dataJ = {
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, null, null]
}
var highchartsData = []; //series data
Object.keys(dataJ).map((el) => {
highchartsData.push({
name: el,
data: dataJ[el].map(Number) //convert to number
})
})
var dataJ = {
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, null, null]
}
var highchartsData = [];
Object.keys(dataJ).map((el) => {
highchartsData.push({
name: el,
data: dataJ[el].map(Number)
})
})
Highcharts.chart('container', {
title: {
text: 'Solar Employment Growth by Sector, 2010-2016'
},
subtitle: {
text: 'Source: thesolarfoundation.com'
},
yAxis: {
title: {
text: 'Number of Employees'
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
series: highchartsData,
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
});
#container {
min-width: 310px;
max-width: 800px;
height: 400px;
margin: 0 auto
}
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/series-label.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container"></div>

After looking at your fiddle I think I know what's wrong. Your server response object has the data as an array of strings. Apparently highcharts will not automatically parse these strings as numbers, but you can map the array to numbers as such:
series: [{
name: 'A',
data: myo['A'].map(parseInt)
}, {
name: 'B',
data: myo['B'].map(parseInt)
}, {
name: 'C',
data: myo['C'].map(parseInt)
}]

Related

Remove redundant legends on the chart using generateLabels with ChartJS v3

I have a chart as follows, I only need one pair of legends (data 1 and data 2) to show on the chart.
In ChartJS v2, I can use generateLabels like this but it doesn't seem to work the same way in v3 that I'm using.
Is there an easy way to achieve this in v3 using generateLabels or do I have to change the DOM structure?
const trend_options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
datalabels: {
formatter: function(value, ctx) {
const otherDatasetIndex = ctx.datasetIndex % 2 === 0 ? ctx.datasetIndex + 1 : ctx.datasetIndex - 1;
const total = ctx.chart.data.datasets[otherDatasetIndex].data[ctx.dataIndex] + value;
return `${(value / total * 100).toFixed()}%`;
},
font: {
weight: "bold"
},
color: "#fff",
display: function(context) {
let index = context.dataIndex;
let value = context.dataset.data[index];
return value > 0; // display labels with a value greater than 0
}
},
},
scales: {
x: {
stacked: true
},
y: {
stacked: true,
suggestedMax: 15,
ticks: {
beginAtZero: true,
stepSize: 5,
}
}
},
};
const app_data_trend = [{
label: 'data 1',
data: [3, 4, 5, 6, 4, 4, 4],
backgroundColor: '#007bff',
stack: 'Stack 0',
},
{
label: 'data 2',
data: [3, 4, 5, 4, 3, 2, 3],
backgroundColor: '#6DB526',
stack: 'Stack 0',
},
{
label: 'data 1',
data: [4, 4, 4, 3, 2, 5, 4],
backgroundColor: '#007bff',
stack: 'Stack 1',
},
{
label: 'data 2',
data: [4, 2, 5, 2, 3, 3, 4],
backgroundColor: '#6DB526',
stack: 'Stack 1',
},
{
label: 'data 1',
data: [2, 4, 5, 2, 4, 5, 3],
backgroundColor: '#007bff',
stack: 'Stack 2',
},
{
label: 'data 2',
data: [1, 1, 2, 4, 4, 3, 5],
backgroundColor: '#6DB526',
stack: 'Stack 2',
},
]
const ctx8 = document.getElementById("tsa-applications-trending");
const chart8 = new Chart(ctx8, {
plugins: [ChartDataLabels],
type: 'bar',
data: {
labels: ['person1', 'person2', 'person3', 'person4'],
datasets: app_data_trend
},
options: trend_options,
});
.chart-container{
position: relative; height:80vh; width:80vw
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.7.1/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.0.0/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#2.0.0"></script>
<div class="chart-container">
<canvas id="tsa-applications-trending"></canvas>
</div>
options.plugins.legend.labels.generateLabels can still be used in Chart.js v3. For your case, this could look as follows:
generateLabels: chart => chart.data.labels.slice(0, 2).map((l, i) => ({
datasetIndex: i,
text: l,
fillStyle: chart.data.datasets[i].backgroundColor,
strokeStyle: chart.data.datasets[i].backgroundColor,
hidden: chart.getDatasetMeta(i).hidden
}))
const trend_options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
datalabels: {
formatter: function(value, ctx) {
const otherDatasetIndex = ctx.datasetIndex % 2 === 0 ? ctx.datasetIndex + 1 : ctx.datasetIndex - 1;
const total = ctx.chart.data.datasets[otherDatasetIndex].data[ctx.dataIndex] + value;
return `${(value / total * 100).toFixed()}%`;
},
font: {
weight: "bold"
},
color: "#fff",
display: function(context) {
let index = context.dataIndex;
let value = context.dataset.data[index];
return value > 0; // display labels with a value greater than 0
}
},
legend: {
labels: {
generateLabels: chart => chart.data.labels.slice(0, 2).map((l, i) => ({
datasetIndex: i,
text: l,
fillStyle: chart.data.datasets[i].backgroundColor,
strokeStyle: chart.data.datasets[i].backgroundColor,
hidden: chart.getDatasetMeta(i).hidden
}))
}
},
},
scales: {
x: {
stacked: true
},
y: {
stacked: true,
suggestedMax: 15,
ticks: {
beginAtZero: true,
stepSize: 5,
}
}
},
};
const app_data_trend = [{
label: 'data 1',
data: [3, 4, 5, 6, 4, 4, 4],
backgroundColor: '#007bff',
stack: 'Stack 0',
},
{
label: 'data 2',
data: [3, 4, 5, 4, 3, 2, 3],
backgroundColor: '#6DB526',
stack: 'Stack 0',
},
{
label: 'data 1',
data: [4, 4, 4, 3, 2, 5, 4],
backgroundColor: '#007bff',
stack: 'Stack 1',
},
{
label: 'data 2',
data: [4, 2, 5, 2, 3, 3, 4],
backgroundColor: '#6DB526',
stack: 'Stack 1',
},
{
label: 'data 1',
data: [2, 4, 5, 2, 4, 5, 3],
backgroundColor: '#007bff',
stack: 'Stack 2',
},
{
label: 'data 2',
data: [1, 1, 2, 4, 4, 3, 5],
backgroundColor: '#6DB526',
stack: 'Stack 2',
},
]
const ctx8 = document.getElementById("tsa-applications-trending");
const chart8 = new Chart(ctx8, {
plugins: [ChartDataLabels],
type: 'bar',
data: {
labels: ['person1', 'person2', 'person3', 'person4'],
datasets: app_data_trend
},
options: trend_options,
});
.chart-container {
position: relative;
height: 80vh;
width: 80vw
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.7.1/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels#2.0.0"></script>
<div class="chart-container">
<canvas id="tsa-applications-trending"></canvas>
</div>
The problem with the approach is, that the displayed legend elements are still tied to individual datasets only. If you want to show/hide other datasets as well, you also need to define a legend.onClick function, similar to the one from this answer.

Data map to suitable Highcharts series

In my aplication I am using Highcharts and I have and data like from API
var data=[
{Part1:6,Part2:3,Test2:7,Part4:4,Part5:2},
{Part1:8,Part2:5,Test2:8,Part4:6,Part5:7,Part6:2},
{Part1:8,Part2:5,Test2:8,Part4:6,Part5:7,Part6:2,Test:3}
]
and I need to convert it to
[
{ name:"Test", data: [null, null, 3] },
{ name:"Part6", data: [null, 2, 2] },
{ name:"Part5", data: [2, 7, 7] },
{ name:"Part4", data: [4, 6, 6] },
{ name:"Test2", data: [7, 8, 8] },
{ name:"Part2", data: [3, 5, 5] },
{ name:"Part1", data: [6, 8, 8] }
];
this is my example app snipped
var data=[
{Part1:6,Part2:3,Test2:7,Part4:4,Part5:2},
{Part1:8,Part2:5,Test2:8,Part4:6,Part5:7,Part6:2},
{Part1:8,Part2:5,Test2:8,Part4:6,Part5:7,Part6:2,Test:3}
]
var expected_result=
[
{name: 'Test', data: [null, null, 3] },
{name: 'Part6', data: [null, 2,2] },
{name: 'Part5', data: [2, 7, 7] },
{name: 'Part4', data: [4, 6, 6] },
{name: 'Test2', data: [7, 8, 8] },
{name: 'Part2', data: [3, 5, 5] },
{name: 'Part1', data: [6, 8, 8] }
];
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Stacked column chart'
},
xAxis: {
categories: ['Net', 'Brut', 'Others']
},
tooltip: {
headerFormat: '<b>{point.x}</b><br/>',
pointFormat: '{series.name}: {point.y}<br/>Total: {point.stackTotal}'
},
plotOptions: {
column: {
stacking: 'normal',
dataLabels: {
enabled: true
}
}
},
series: expected_result
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<figure class="highcharts-figure">
<div id="container"></div>
</figure>
How can I convert my base data to expected. Thanks in advance
const data = [
{ Part1: 6, Part2: 3, Part3: 7, Part4: 4, Part5: 2 },
{ Part1: 8, Part2: 5, Part3: 8, Part4: 6, Part5: 7, Part6: 2 },
{ Part1: 8, Part2: 5, Part3: 8, Part4: 6, Part5: 7, Part6: 2, Part7: 3 },
];
const maxNumberPart = Math.max.apply(
null,
data.map(d => Object.keys(d).length),
);
const result = new Array(maxNumberPart).fill(null).map((p, i) => ({ name: `Part${maxNumberPart - i}`, data: [] }));
data.forEach(obj => {
result.forEach(part => {
part.data.push(obj[part.name] ? obj[part.name] : null);
});
});
console.log(result);
UPD dynamic key names:
const data = [
{ Part1: 6, Part2: 3, Test2: 7, Part4: 4, Part5: 2 },
{ Part1: 8, Part2: 5, Part3: 8, Part4: 6, Part5: 7, Part6: 2 },
{ Part1: 8, Part2: 5, Part3: 8, Test: 6, Part5: 7, Part6: 2, Part7: 3 },
];
const keys = data.reduce((set, val) => {
Object.keys(val).forEach(key => set.add(key));
return set;
}, new Set());
const result = [...keys].map(key => ({
name: key,
data: [],
}));
data.forEach(obj => {
result.forEach(part => {
part.data.push(obj[part.name] ? obj[part.name] : null);
});
});
console.log(result);
You can group numbers first using a foreach and then fill the null value after
you can try this it is key agnostic
var data=[
{Part1:6,part2:3,part3:7,part4:4,part5:2},
{Part1:8,part2:5,part3:8,part4:6,part5:7,part6:2},
{Part1:8,part2:5,part3:8,part4:6,part5:7,part6:2,part7:3}
]
p={}
res=[]
for(let o of data ){
Object.entries(o).forEach(k=>{
if(!p[k[0]]) res.push({name:k[0],data:p[k[0]]=[]})
p[k[0]].push(k[1])
})
}
i=0
while(i<res.length){
while(res[i].data.length<3){
res[i].data.unshift(null)}
i++
}
console.log(res.reverse())
if your keys are not the same and each time they can be different you can
try this
var data=[
{Test:6,part2:3,part3:7,part4:4,part5:2},
{Part1:8,part2:5,part3:8,part4:6,part5:7,part6:2},
{Part1:8,part2:5,part3:8,part4:6,part5:7,part6:2,part7:3}
]
p={}
res=[]
for(let o of data ){
obarr= Object.entries(o)
obarr.forEach((k)=>{
if(!p[obarr.indexOf(k)]) res.push({name:k[0],data:p[obarr.indexOf(k)]=[]})
p[obarr.indexOf(k)].push(k[1])
})
}
i=0
while(i<res.length){
while(res[i].data.length<3){
res[i].data.unshift(null)}
i++
}
console.log(res.reverse())

Using Trellis Chart with Stacked Columns in Highcharts

I have a time series data which I want to show as a trellis chart:
Here is a basic version of Trellis Chart from the Highcharts website:
Instead of Erik, Gert, Helge, Torstein, I am working to replace it with groups: Client, DII, FII, PRO.
In this chart below, fruits name is a simple series but here I have a time series data: Fut Index Longs and Fut Index Shorts.
Each of the above groupings( Client, DII...) have their own version of Fut Index Longs and Fut Index Shorts.
I want to show Fut Index Longs, Fut Index Shorts as stacked with the x-axis denoting the different days.
I have tried to use a nested series to accomplish this, but there is no data being displayed. Here is my source:
var charts = [],
$containers = $('#trellis td'),
datasets = [
{
name: 'Client',
data:
[
{
name: "Fut Index Longs",
data: [["2014-02-10", 5], ["2014-02-11", 9], ["2014-02-12", 7]]
},
{
name: "Fut Index Shorts",
data: [["2014-02-10", 5], ["2014-02-11", 9], ["2014-02-12", 2]]
}
]
},
{
name: 'DII',
data:
[
{
name: "Fut Index Longs",
data: [["2014-02-10", 5], ["2014-02-11", 9], ["2014-02-12", 7]]
},
{
name: "Fut Index Shorts",
data: [["2014-02-10", 5], ["2014-02-11", 9], ["2014-02-12", 2]]
}
]
},
{
name: 'FII',
data:
[
{
name: "Fut Index Longs",
data: [["2014-02-10", 5], ["2014-02-11", 9], ["2014-02-12", 7]]
},
{
name: "Fut Index Shorts",
data: [["2014-02-10", 5], ["2014-02-11", 9], ["2014-02-12", 2]]
}
]
},
{
name: 'PRO',
data:
[
{
name: "Fut Index Longs",
data: [["2014-02-10", 5], ["2014-02-11", 9], ["2014-02-12", 7]]
},
{
name: "Fut Index Shorts",
data: [["2014-02-10", 5], ["2014-02-11", 9], ["2014-02-12", 2]]
}
]
}
];
$.each(datasets, function(i, dataset) {
charts.push(new Highcharts.Chart({
chart: {
renderTo: $containers[i],
type: 'bar',
marginLeft: i === 0 ? 100 : 10
},
title: {
text: dataset.name,
align: 'left',
x: i === 0 ? 90 : 0
},
credits: {
enabled: false
},
xAxis: {
type: 'datetime'
},
yAxis: {
allowDecimals: false,
title: {
text: null
},
min: 0,
max: 10
},
plotOptions: {
column: {
stacking: 'normal'
}
},
legend: {
enabled: false
},
series: [dataset]
}));
});
The link to jsfiddle: http://jsfiddle.net/g6jLhux2/
What am I doing wrong?
In your case, you need to use
series: dataset.data
Also, your x-axis are not datetime but categories, because datetime are supposed to be passed as numbers (milliseconds). You must then use:
xAxis: {
type: 'category'
},
if you want to properly show the data in your charts. See updated jsfiddle
Does it help you ?

After Drilldown, Drillup button overlaps with chart

I have a Highcharts column chart with drilldown and when I do drill down there is a problem. When I drill down the drill up button appears with overlapping with the chart. Is there a way to place this drill up button without overlapping with the chart.
Following is a sample code.
http://jsfiddle.net/yasirunilan/gt8n96ck/
// Create the chart
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Highcharts multi-series drilldown'
},
subtitle: {
text: 'Click columns to drill down to single series. Click categories to drill down both.'
},
xAxis: {
type: 'category'
},
plotOptions: {
series: {
borderWidth: 0,
dataLabels: {
enabled: true
}
}
},
series: [{
name: '2010',
data: [{
name: 'Republican',
y: 5,
drilldown: 'republican-2010'
}, {
name: 'Democrats',
y: 2,
drilldown: 'democrats-2010'
}, {
name: 'Other',
y: 4,
drilldown: 'other-2010'
}]
}, {
name: '2014',
data: [{
name: 'Republican',
y: 4,
drilldown: 'republican-2014'
}, {
name: 'Democrats',
y: 4,
drilldown: 'democrats-2014'
}, {
name: 'Other',
y: 4,
drilldown: 'other-2014'
}]
}],
drilldown: {
series: [{
id: 'republican-2010',
data: [
['East', 4],
['West', 2],
['North', 1],
['South', 4]
]
}, {
id: 'democrats-2010',
data: [
['East', 6],
['West', 2],
['North', 2],
['South', 4]
]
}, {
id: 'other-2010',
data: [
['East', 2],
['West', 7],
['North', 3],
['South', 2]
]
}, {
id: 'republican-2014',
data: [
['East', 2],
['West', 4],
['North', 1],
['South', 7]
]
}, {
id: 'democrats-2014',
data: [
['East', 4],
['West', 2],
['North', 5],
['South', 3]
]
}, {
id: 'other-2014',
data: [
['East', 7],
['West', 8],
['North', 2],
['South', 2]
]
}]
}
});
In following way issue can be reproduced.
Select 2010 from the Legend, then click on republicans to drill down. The chart gets overlapped with button.
To avoid overlapping the button with a chart, you can move drillUpButton outside the plot area:
drillUpButton: {
position: {
x: 0,
y: -35,
}
}
Live demo: http://jsfiddle.net/BlackLabel/v8azqpo3/
API: https://api.highcharts.com/highcharts/drilldown.drillUpButton.position.y

Highcharts - line between two categories

I have a chart that is of type column range and my requirement is to have a line connecting two categories.
JsFiddle
$(function() { $('#container').highcharts({
chart: {
type: 'columnrange',
inverted: true
},
title: {
text: 'Test'
},
subtitle: {
text: 'Sample'
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar'],
visible: false
},
yAxis: {
visible: false
},
legend: {
enabled: false
},
series: [{
name: 'Series1',
data: [
[0, 3],
[0, 3],
[0, 3]
],
pointPlacement: -0.20,
pointWidth: 50
}, {
name: 'Series2',
data: [
[3, 6],
[3, 6],
[3, 6]
],
pointPlacement: 0,
pointWidth: 1
}, {
name: 'Series3',
data: [
[6, 9],
[6, 9],
[6, 9]
],
pointPlacement: 0.20,
pointWidth: 50
}] });});
How do I draw a line from one category to another?
Is there any property available?
You should be able to achieve similar chart by simply adding new line series to your chart:
{
name: 'Series4',
type: 'line',
marker: {
enabled: false
},
index: 1,
data: [
[0, 1.5],
[1, 1.5],
[2, 1.5]
],
},
Here you can see an example how this chart may work: http://jsfiddle.net/ebtygovh/6/
You can also use renderer.path for adding lines to your chart: http://api.highcharts.com/highcharts/Renderer.path

Categories