Im working with chartJS to populate graphs using a large recordset.
Im creating an ArrayOfArrays like follows:
var arrayOfArrays =
JSON.parse('#Html.Raw(Json.Encode(Model.organisationData))');
I am now trying to create multiple arrays to store the data to populate the graph. To do so I am doing the following:
var array0=[]
var array1=[]
var array2=[]
var array3=[]
...etc...up to var array17=[]
I populate each array like follows:
for (var i = 0; i < 5; i++) {
array1.push(arrayOfArrays[i]['dataRows'][1]['dataCount']);
}
array1.push(arrayOfArrays[1]['dataRows'][1]['dataLabel']); //Item 5 - Label
for (var i = 0; i < 5; i++) {
array2.push(arrayOfArrays[i]['dataRows'][2]['dataCount']);
}
array2.push(arrayOfArrays[1]['dataRows'][2]['dataLabel']);//Item 5 - Label
etc..etc...repeated 17 times....
Then to populate the chart I'm doing..
var options = {
type: 'bar',
data: {
datasets: [
{
label: #array0[5]",
data: #array0
},
{
label: #array1[5]",
data: #array1
},
{
label: #array2[5]",
data: #array2
}
.....etc....etc.....17 times...
]
},
options: {
scales: {
yAxes: [{
ticks: {
reverse: false
}
}]
}
}
}
Is there a more efficient way of doing this? Instead of repeating everything 17 times can i create a loop that generates each of the arrays. Then populate those arrays and use them in chartJS.
Related
I have an array that contains multiple objects and each object has an array called data that contains multiple data.
"datasets": [
{
data: [1227.0, 698.4, 2903.1, 7280.2, 5447.9]
},
{
data: [302.0, 170.7, 592.2, 1293.6, 961.3]
},
{
data: [239.0, 275.5, 353.5, 478.0, 576.9]
},
...
]
For each data array that I have, how can I write a logic so that I store the values for each matching index into a new array data set. For example, I need to generate a new array which would only contains the values at index zero like so:
[1227.0, 302.0, 239.0]
and then another array which would contain the values at index one only
[698.4, 170.7, 275.5]
The desired output that I need is the following:
"result": [
{
data: [1227.0, 302.0, 239.0]
},
{
data: [698.4, 170.7, 275.5]
},
{
data: [2903.1, 592.2, 353.5]
},
{
data: [7280.2, 1293.6, 478.0]
},
{
data: [5447.9, 961.3, 576.9]
}
]
How will I achieve this. Can someone please help me out?
It looks like you need to transpose your data. Here's one possible solution:
Assuming this was nested inside an object called dataset:
Ex:
const dataSet = {
"datasets": [
{
data: [1227.0, 698.4, 2903.1, 7280.2, 5447.9]
},
{
data: [302.0, 170.7, 592.2, 1293.6, 961.3]
},
{
data: [239.0, 275.5, 353.5, 478.0, 576.9]
},
...
]
}
Now, this is a bit of a cumbersome process, but the solution would involve:
Iterating through every element object of dataSet["datasets"]
Create a new array upon every increment of i
Stopping at the jth element of dataSet["datasets"][i].data[j] and store that it in the array instance
When you've went through every object element's jth position, push that array instance into an output array.
Here's how one solution would look (O(n^2)) :
const matrixObjTranspose = (matrixObj) => {
const output = [];
for (let i = 0; i < matrixObj.datasets[0].data.length; i += 1) {
const newSubArr = [];
for (let j = 0; j < matrixObj.datasets.length; j += 1) {
newSubArr.push(matrixObj.datasets[j].data[i]);
}
output.push(newSubArr);
}
return output;
};
console.log(matrixObjTranspose(dataSet))
const c = [
{
data: [1227.0, 698.4, 2903.1, 7280.2, 5447.9]
},
{
data: [302.0, 170.7, 592.2, 1293.6, 961.3]
},
{
data: [239.0, 275.5, 353.5, 478.0, 576.9]
}]
const convert = (arr) => {
return arr.slice(1).reduce((sum, cur) => {
return sum.map((v, i) => (Array.isArray(v) ? v : [v]).concat(cur.data[i]))
}, arr[0].data).map(v => ({data: v}))
}
console.log(convert(c))
I've found a weird behavior of a site that I'm developing. It is being developed using Laravel 5.8 and uses the React preset to build all the front end. This project has a section of stats, and in some of them you need to show a total field.
When I use the php artisan serve to run my project, and access the stats, the results are displayed and calculated correctly. The problem comes when I deploy this site on Apache using AMPPS. When I do this, the total is calculated as a string, so for example, if I have a sum of 1+0, instead of get a 1, I'm getting a 10. It is concatenating the integers as strings.
This is the result when I'm using the php artisan serve
And this one when I use apache:
This is my client code:
constructor(props) {
super(props);
this.statsRoute = 'attendants/classification';
this.state = {
barData: {
labels: [],
datasets: [
{
backgroundColor: [],
borderWidth: 1,
hoverBackgroundColor: [],
data: []
}
],
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
legend: {
display: false,
}
},
tableRepresentation: {
header: ['Clasificación', 'Número de Asistentes'],
rows: []
}
},
// Pie chart data
pieData: {
labels: [],
datasets: [
{
backgroundColor: [],
borderWidth: 1,
hoverBackgroundColor: [],
data: [],
total: 0
}
],
options: {
legend: {
display: true,
position: 'bottom',
},
tooltips: {
callbacks: {
label: function (tooltipItem, data) {
let dataset = data.datasets[tooltipItem.datasetIndex];
let currentValue = dataset.data[tooltipItem.index].toFixed(2);
let label = data.labels[tooltipItem.index];
return ` ${label}: ${currentValue}%`;
}
}
}
},
tableRepresentation: {
header: ['Clasificación', 'Porcentaje'],
rows: []
}
}
}
}
componentWillMount() {
this.props.statService.getStats(this.statsRoute)
.then(res => {
let total = 0;
let barData = this.state.barData;
let pieData = this.state.pieData;
let colors = this.props.randomColorService.getArrayOfColors(res.length);
for ( let i = 0; i < res.length; i++ ) {
let item = res[i];
total += item.count;
barData.tableRepresentation.rows.push([
item.classification,
item.count
]);
barData.labels.push(item.classification);
barData.datasets[0].data.push(item.count);
pieData.datasets[0].data.push(item.count * 100);
}
// Assign labels and colors to pie chart
pieData.labels = barData.labels;
barData.datasets[0].backgroundColor = colors['withAlfa'];
barData.datasets[0].hoverBackgroundColor = colors['withoutAlfa'];
// Assign colors to bar chart
pieData.datasets[0].backgroundColor = colors['withAlfa'];
pieData.datasets[0].hoverBackgroundColor = colors['withoutAlfa'];
for ( let i = 0; i < pieData.datasets[0].data.length; i++ ) {
pieData.datasets[0].data[i] /= total;
let value = pieData.datasets[0].data[i];
pieData.tableRepresentation.rows.push([
pieData.labels[i],
`${value.toFixed(2)}%`
]);
}
barData.tableRepresentation.rows.push([
"",
`Total: ${total}`
]);
this.setState({
barData: barData,
pieData: pieData
});
})
.catch(err => {
console.log(err);
});
}
And I doubt this is a problem from the backend, because when I do the query I got a JSON like this:
{
"data": [
{
"item": "Invitado",
"count": 6
},
{
"item": "Asistente",
"count": 7
}
]
}
So, the backend does not make the total sum, I do that on the client.
Why this behaviour only happens when I'm using apache as server?
Try replacing total += item.count; with total += +item.count;.
+ will explicitly convert it into int, right now for the apache version, the numbers are being treated as strings and are concatenated rather than added.
I am creating a chart with Chartist.js. I'm getting json data with the Google embed API. I have a problem with this one. The array works with the values I give. But it does not work for data from json.
my code :
var TotalBrowser = [];
var BrowserSeries = [];
var oxyn = {
queryAnalytics: function() {
var id = '164690638';
var expressions = [{
expression: 'ga:hits'
}];
var dimension = [{
name: 'ga:browser'
}];
oxyn.getReportQuery(id, '7daysago', 'today', expressions, dimension).then(function(response) {
var formattedJson = JSON.stringify(response.result, null, 2);
var data = JSON.parse(formattedJson);
var i = 0;
BrowserTotal = data.reports[0].data.totals[0].values[0];
jQuery(data.reports[0].data.rows).each(function() {
if (i <= 3) {
jQuery('#Browsers').append(browsericon[i] + this.dimensions[0]);
var percent = (parseInt(this.metrics[0].values[0]) / parseInt(BrowserTotal)) * 100;
BrowserSeries.push(Math.round(percent));
TotalBrowser.push(Math.round(percent) + '%');
i++;
}
});
demo.initChartist();
});
}
}
var demo = {
initChartist: function() {
var dataPreferences = {
series: [
[BrowserSeries.join()]
]
};
var optionsPreferences = {
donut: true,
donutWidth: 40,
startAngle: 0,
total: 100,
showLabel: false,
axisX: {
showGrid: false
}
};
Chartist.Pie('#chartPreferences', dataPreferences, optionsPreferences);
Chartist.Pie('#chartPreferences', {
labels: [TotalBrowser.join()],
series: [BrowserSeries.join()]
});
console.log(BrowserSeries.join());
}
};
it does not work that way. But if I write the code like this, it works.
Chartist.Pie('#chartPreferences', {
labels: [TotalBrowser.join()],
series: [30, 70]
});
and this is working.
Chartist.Pie('#chartPreferences', {
labels: [TotalBrowser[0], TotalBrowser[1]],
series: [BrowserSeries[0], BrowserSeries[1]]
});
console output
console.log(BrowserSeries.join());
30,70
JSON Source
It's a very silly problem.
yes I finally solved it. I write for those who have the same problem.
Chartist.Pie('#chartPreferences', {
labels: TotalBrowser,
series: BrowserSeries
});
We need to remove [ ] characters. We must also send the data directly to the array.
Also : https://github.com/gionkunz/chartist-js/issues/738
I'm using JQuery, ChartJS, Moment.js gathering data in JSON format for multiple charts on the same page, but from the same JSON source. In the JSON the height objects are one graph and the lengths another one.
This is an example of how the JSON looks
"Series": {
"heights": [
{
"Date": "2014-10-01",
"Value": 22
},
{
"Date": "2014-10-01",
"Value": 53
},
{
"Date": "2014-10-01",
"Value": 57
},
],
"lengths": [
{
"Date": "2014-10-01",
"Value": 54
},
{
"Date": "2014-10-01",
"Value": 33
}
]
}
I've managed to loop through the JSON to display each graph but I'm not really able to do it using the "DRY - Don't repeat yourself" way. Which now I have large chunks of code that is hard to update/read.
$.getJSON("data.json", function(data) {
var dateArray = [];
var valueArray = [];
for ( var i = 0; i < data.Series["heights"].length; i++) {
var obj = data.Series.heights[i];
var date = obj["Date"].toString();
var Value = obj["Value"];
for ( var key in obj) {
//console.log(obj["Value"]);
//date = obj["Date"];
Value = obj[key].toString();
}
valueArray.push(Value);
dateArray.push(moment(date).format("MMMM Mo"));
var dataArray = {
labels: dateArray,
datasets: [
{
label: "Lengths",
strokeColor: "rgb(26, 188, 156)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: valueArray
}
]
};
}
var ctx = document.getElementById("lengthsChart").getContext("2d");
var myLineChart = new Chart(ctx).Line(dataArray, {
scaleShowGridLines : true,
bezierCurve : true,
bezierCurveTension : 0.4,
datasetStroke : false,
fillColor: "rgba(0,0,0,0)",
datasetFill : false,
responsive: true,
showTooltips: true,
animation: false
});
});
Right now I have this code in a switch statement for "heights", "lengths" etc... Which I guess is a horrible way to do it. But I've been unable to make a loop for the individual charts.
I've tried things like this:
for(var x in measurement) {
console.log(measurement[x]);
for ( var i = 0; i < data.Series.hasOwnProperty(measurement).length; i++) {
var obj = data.Series.hasOwnProperty(measurement)[i];
var date = obj["Date"].toString();
var Value = obj["Value"];
console.log(date, Value);
}
But I'm unable to get it to work, to loop through the data.Series. /heights/lengths../ [i]
I'm very thankful for tips how to accomplish this.
Thanks!
If you replace measurement with data.Series and get rid of the hasOwnProperty(measurement) thing, you are almost there. The only thing you need is a way to keep the transformation from a list of {Date, Value} objects to a pair of list of dates and value for each serie.
var series = {};
// This loop is looping across all the series.
// x will have all the series names (heights, lengths, etc.).
for (var x in data.Series) {
var dates = [];
var values = [];
// Loop across all the measurements for every serie.
for (var i = 0; i < data.Series[x].length; i++) {
var obj = data.Series[x][i];
// Assuming that all the different series (heights, lengths, etc.) have the same two Date, Value attributes.
dates.push(obj.Date);
values.push(obj.Value);
}
// Keep the list of dates and values by serie name.
series[x] = {
dates: dates,
values: values
};
}
series will contain this:
{
heights: {
dates: [
'2014-10-01',
'2014-10-01',
'2014-10-01'
],
values: [
22,
53,
57
]
},
lengths: {
dates: [
'2014-10-01',
'2014-10-01'
],
values: [
54,
33
]
}
}
So you can use them like this:
console.log(series);
console.log(series.heights);
console.log(series.heights.dates);
console.log(series.heights.values);
console.log(series.lengths);
console.log(series.lengths.dates);
console.log(series.lengths.values);
I am trying to add a property to javascript nested array object...
I need to traverse the tree get the value of text property and convert it to lowercase and add this data as new property (lowerText)
Old array:
var oldObject= [{
text: "Subgroup3",
items: [{
text: "subgroup5",
items: [{
text: "subgroup6",
items: [{
text: "subgroup7",
items: [{
text: "subgroup8"
}]
}]
}]
}]
}]
I need the new array object as below:
var newObject= [{
text: "Subgroup3",
lowerText:"subgroup3",
items: [{
text: "subgroup5",
lowerText:"subgroup5",
items: [{
text: "subgroup6",
lowerText:"subgroup6",
items: [{
text: "subgroup7",
lowerText:"subgroup7",
items: [{
text: "subgroup8",
lowerText:"subgroup8",
}]
}]
}]
}]
}]
This is what I tried, looping through each object and passing the items (array) to recursive function to set the property but it doesn't seem to work fine. Not sure what I am doing wrong, can someone please help me with this code?
for (var i = 0; i < data.length; i++) {
data[i].lowerText=data[i].text.toLowerCase();
loopTree(data[i].items);
}
function loopTree(node){
if (node) {
$.each(node, function (idx, item) {
item.lowerText=item.text.toLowerCase();
if(item.items){
loopTree(item.items)
}
});
}
}
EDIT: Below code did the job.
for (var i = 0; i < data.length; i++) {
if(data[i]){
process( data[i]);
}
}
function process(val) {
val.lowerText = val.text.toLowerCase();
if(val.items){
for(var i = 0, len = val.items.length; i < len; i++) {
process(val.items[i]);
}
}
}
If you don't want to clone the objects and just modify the existing ones, try this:
function process(val) {
val.lowerText = val.text.toLowerCase();
for(var i = 0, len = val.items.length; i < len; i++) {
process(val.items[i]);
}
}
process(obj);
If you want to preserve the value in the old array, just push the new object onto the old array:
oldObject.push(newObject[0]);
If you just need to replace the entire array, its trivial,
oldObject = newObject;