Format "data" and "name" highchart mvc asp.net - javascript

I'm in a MVC application, and I'm having problems to generate a chart in highchart, I have this code
foreach (var item in query)
{
object[] values = new object[3];
values[0] = i;
values[1] = Convert.ToDecimal(item.ini) ;
values[2] = Convert.ToDecimal(item.ir);
dataResult.Add(values);
}
generates the following:
passed to the json which returns me this chart
What I like is that the value of "IRI201308NF3" was the name of the series and the other two values ​​gerarariam the correct chart.
Under the Json
$.getJSON("/GrafLev/GetDadosByGraficos", { parameter },
function (data) {
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
ignoreHiddenSeries: false
},
yAxis: {
title: {
text: 'Exchange rate'
},
plotLines: [{
value: limInferior,
color: 'green',
dashStyle: 'shortdash',
width: 2,
label: {
text: 'Inferior'
}
}, {
value: limSuperior,
color: 'red',
dashStyle: 'shortdash',
width: 2,
label: {
text: 'Superior'
}
}]
},
xAxis: {
},
series: [{ data: data }]
});
});
I found this example, but I can not format.
The only thing is that the graph is a line.

Reading the Highcharts api, it's series parameter is looking for an array of dictionaries with a name and data property. You are passing the data property a mixed array. Now, you could reformat the response JSON in the javascript, but I think it's cleaner to format the JSON properly in the C#:
Dictionary<string, object> dataResult = new Dictionary<string, object>();
dataResult["data"] = new List<object[]>();
dataResult["name"] = i;
foreach (var item in query)
{
object[] values = new object[2];
values[0] = Convert.ToDecimal(item.ini) ;
values[1] = Convert.ToDecimal(item.ir);
((List<object[]>)dataResult["data"]).Add(values);
}
This will result in JSON looking like this:
{"data":[[0,0],[1,10],[2,20],[3,30],[4,40],[5,50],[6,60],[7,70],[8,80],[9,90]],"name":"Hi Mom"}
Now you should be good to pass this to the series parameter as:
series: [ data ]
Note, this JSON is still returning a single series. If you need to return multiple series, just comment and I'll modify the code.
EDITS
For multiple series add an outer list containing each series Dictionary<string, object>.
Here's an example:
List<Dictionary<string, object>> dataResult = new List<Dictionary<string, object>>();
for (int i = 1; i <= 2; i++)
{
Dictionary<string, object> aSeries = new Dictionary<string, object>();
aSeries["data"] = new List<object[]>();
aSeries["name"] = "Series " + i.ToString();
for (int j = 0; j < 10; j++)
{
object[] values = new object[2];
values[0] = j;
values[1] = j * 10 * i;
((List<object[]>)aSeries["data"]).Add(values);
}
dataResult.Add(aSeries);
}
Pass to Highcharts as:
series: data

Related

Get an array of objects from a Razor page with JavaScript/jQuery

I am developing an asp.net core web app.
I am sending data from a razor page to getJson script to display a chart on the page.
When I'm sending an array of anonymous classes, getJson receives data. When I'm sending an array of custom class, getJson receives an array of empty objects that have no properties.
Received OK:
var anonymousarray = new[]
{
new {name = "first", value = 3 },
new {name = "second", value = 6 },
new {name = "third", value = 1 },
};
Received as an array of empty objects:
Test[] testarray = new []
{
new Test {name = "first", value = 3},
new Test {name = "second", value = 6},
new Test {name = "third", value = 1}
};
Test class:
public class Test
{
public int value;
public string name;
}
Handler on Razor page:
public JsonResult OnGetData()
{
Test[] testarray= new[]
{
new Test {name = "first", value = 3},
new Test {name = "second", value = 6},
new Test {name = "third", value = 1}
};
return new JsonResult(testarray);
}
Razor page script:
#section scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
var ctx = document.getElementById('barChart').getContext('2d');
$(function () {
$.getJSON(`?handler=Data`, (data) => {
var labels = [];
var dataresults = [];
$.each(data, function (index, item) {
labels.push(item.name);
dataresults.push(item.value);
});
console.log(labels)
console.log(dataresults)
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Results',
data: dataresults,
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
});
});
</script>
}
Please help me to send an array/list of my custom classes to getJson script. I spent many hours reading articles and posts but could not find a solution.
When I'm sending an array of anonymous classes, getJson receives data. When I'm sending an array of custom class, getJson receives an array of empty objects that have no properties.
Please try to modify your Test class to define value and name as property with get and set accessor, like below.
public class Test
{
public int value {get;set;};
public string name {get;set;};
}
From this doc about "Serialization behavior", you can find:
Fields are ignored.

Create new array in Javascript loop

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.

Sort JavaScript Object by category and then by category messages

I have 4 categories that have to be displayed in order I defined in array below. I am trying to sort by these categories and then sort messages within each category alphabetically.
I introduced for loop in below code, but I am not sure how to add my sorted messages back in messagesL object which is the one that is being passed to rest of the code. Any hints would be greatly appreciated.
Here is my code:
PKG.render = function(work,PKG) {
var data = work.details;
var messagesL = new util.LocalDataSource(data);
messagesL.responseSchema = {
resultsList: "messages",
fields: [
"messageId",
"category",
"message"
]
};
/*there could be more categories, ignore them*/
var categories = ["Mentoring", "Availability", "Affordability", "Scalability", "Info"];
var messagesColumnDefs = [
{ key: "Id", label: "ID"},
{ key: "category", label: "Category"},
{ key: "message", label: "Text"}
];
var messagesConfig = { MSG_EMPTY: 'There are no messages to display.',
MSG_ERROR: 'Error while loading table'
};
var sortedMessagesByCategory;
for (var i = 0; i < categories.length; i++)
{
var messagesByCategory;
for(var j = 0; j < data.messages.length; j++)
{
if(data.messages[j].category === categories[i])
messagesByCategory.push(data.messages[j].message);
}
if(typeof messagesByCategory !== "undefined")
messagesByCategory.sort();
/*not sure how to add sorted messages back in messagesL object*/
}
messagesTable = new widget.DataTable('wrmessagesTable', messagesColumnDefs, messagesL, messagesConfig);
workResultsWidgetCollectionPush(messagesTable);
}

How to convert List of JSON Objects to JSON Array using Java EE?

I am trying to convert some JSON objects to a JSON Array. I am fetching the data from a mysql database. My current code is using this loop:
Main Class:
public class Main
{
ResultSet resultSet=null;
TestDAO testDAO = new TestDAO();
public List<JSONObject> getJsonObject()
{
resultSet = testDAO.getResultSet();
List<JSONObject> resList = JsonServices.getFormattedResult(resultSet);
return resList;
}
public JSONArray getJsonArray() throws Exception {
resultSet = testDAO.getResultSet();
JSONArray jsonArray = new JSONArray();
while (resultSet.next()) {
int total_rows = resultSet.getMetaData().getColumnCount();
JSONObject obj = new JSONObject();
for (int i = 0; i < total_rows; i++) {
obj.put(resultSet.getMetaData().getColumnLabel(i + 1).toLowerCase(), resultSet.getObject(i + 1));
}
jsonArray.put(obj);
}
return jsonArray;
}
public static void main(String args[]) throws Exception
{
Main m = new Main();
List<JSONObject> jObj = m.getJsonObject();
JSONArray jArr = m.getJsonArray();
for(int i=0;i<jArr.length();i++)
{
System.out.println(jArr.get(i));
}
/*for(int i=0;i<jObj.size();i++)
{
System.out.println(jObj.get(i));
}*/
}
}
Service class:
public class JsonServices
{
public static List<JSONObject> getFormattedResult(ResultSet rs)
{
List<JSONObject> resList = new ArrayList<JSONObject>();
try
{
ResultSetMetaData rsMeta = rs.getMetaData();
int columnCnt = rsMeta.getColumnCount();
List<String> columnNames = new ArrayList<String>();
for(int i=1;i<=columnCnt;i++)
{
columnNames.add(rsMeta.getColumnName(i).toUpperCase());
}
while(rs.next())
{
JSONObject obj = new JSONObject();
for (int i=1;i<=columnCnt;i++)
{
String key = columnNames.get(i-1);
String value = rs.getString(i);
obj.put(key, value);
}
resList.add(obj);
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
rs.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
}
return resList;
}
}
My current loop returns data in this format:
{"fineloadinmetric":"648265.32","waterlevel":"45.59","fdate":"2017-08-29","discharge":"43391.657","runinthousand":"156.771","courseloadinmetric":"50538.462","mediumloadinmetric":"176808.134","floodflag":"false"}
{"fineloadinmetric":"856793.621","waterlevel":"44.94","fdate":"2017-08-28","discharge":"46843.674","runinthousand":"212.579","courseloadinmetric":"43957.152","mediumloadinmetric":"132528.51","floodflag":"false"}
{"fineloadinmetric":"898789.111","waterlevel":"45.66","fdate":"2017-08-27","discharge":"43436.507","runinthousand":"159.727","courseloadinmetric":"53053.74","mediumloadinmetric":"169307.45","floodflag":"false"}
I need the JSON array like:
[{
name: 'fineloadinmetric',
data: [648265.32, 52503, 57177, 69658, 97031, 119931, 137133, 154175]
}, {
name: 'waterlevel',
data: [45.59, 24064, 29742, 29851, 32490, 30282, 38121, 40434]
}, {
name: 'fdate',
data: [2017-08-29, 2017-08-28, 2017-08-27, 2017-08-26, 2017-08-25, 2017-08-24, 2017-08-23, 2017-08-22]
}, {
name: 'discharge',
data: [43391.657, null, 7988, 12169, 15112, 22452, 34400, 34227]
}, {
name: 'runinthousand',
data: [156.771, 5948, 8105, 11248, 8989, 11816, 18274, 18111]
}, {
name: 'courseloadinmetric',
data: [50538.462, 5948, 8105, 11248, 8989, 11816, 18274, 18111]
}, {
name: 'mediumloadinmetric',
data: [176808.134, 5948, 8105, 11248, 8989, 11816, 18274, 18111]
}, {
name: 'floodflag',
data: [false, true, false, true, false, false, false, true]
}]
Please ignore the data, it's from a different sample.
I will be using this JSON Array to geenrate some charts using Highcharts.js.
Looking at the sort of objects you want to return, I'd first populate a Map<String,JSONArray> with the "data" arrays. So iterate over the output you currently get with this
Map<String,JSONArray> map=new HashMap<>();
for(int i=0;i<jArr.length();i++){
JSONObject object=(JSONObject)jArr.get(i);
for(Iterator<String> it=object.keys();it.hasNext();){
String key=it.next();
if(!map.containsKey(key)){//This initializes the array on first element
map.put(key, new JSONArray());
}
//This fetches the array corresponding to the member's name from the map
JSONArray array=map.get(key);
//put the member's value into the array
array.put(object.get(key));
}
}
Now, map's entry set looks pretty much like the desired output you posted. The only thing left to do is to convert that entry set into a JSONArray
To do this, get map's entry set with map.entrySet(), which returns a Set<Entry<String,JSONArray>, which you can iterate over with a for-each loop. Calling an entry's getKey() returns the String the element maps, which goes into name in a JSONObject like the ones you say you want. Calling the entry's getValue() returns the JSONArray built by the loop, which goes into data. Add these objects to a JSONArray and you have what you're looking for.

How to create an average line for an irregular time graph?

I'm building irregular time graphs with HighCharts that at the moment look like so:
And I'm wondering if it's possible to create an 'average' line for the three (or possibly more in future) lines.
It would start following the blue line, then go closer to the green line mid-January, etc.
At the moment the code I'm working with looks like:
$('#chart').highcharts({
chart: { type: 'spline' },
title: { text: '' },
xAxis: { type: 'datetime' },
yAxis: {
title: { text: '' }
}
series: [{
name: 'Line 1',
data: [
[Date.UTC(2014,0,16), 173.33],
[Date.UTC(2014,0,23), 163.33],
[Date.UTC(2014,0,30), 137.67],
[Date.UTC(2014,1,6), 176.33],
[Date.UTC(2014,1,13), 178.67],
[Date.UTC(2014,1,27), 167.33],
],
color: 'purple'
},
{
name: 'Line 2',
data: [
[Date.UTC(2014,0,11), 156.33],
[Date.UTC(2014,1,15), 167.67],
],
color: 'green'
},
{
name: 'Line 3',
data: [
[Date.UTC(2014,0,1), 135],
[Date.UTC(2014,0,5), 146.33],
[Date.UTC(2014,0,27), 146.75],
],
color: 'blue'
}]
});
What you are describing is called a trend or regression line. Highcharts doesn't have a built in ability to add these lines, but the math isn't too difficult (and besides, it's more fun to do it yourself). I've coded up the simplest example I can using least squared linear regression.
/////////////////////
//utility functions//
////////////////////
// linear regression
// given array of x values and array of y values
// returns rV object with slope/intercept
lineFit = function(xs, ys, rV){
rV.slope = 0.0;
rV.intercept = 0.0;
rV.rSquared = 1.0; // assume perfection
if (xs.length < 2)
{
return false;
}
if (xs.Count != ys.Count)
{
return false;
}
var N = xs.length;
var sumX = sumFunc(xs,null);
var sumY = sumFunc(ys,null);
var funcSq = function(i){return (i*i);}
var funcSq2 = function(i,j){return (i*j);}
var sumXx = sumFunc(xs, funcSq);
var sumYy = sumFunc(ys, funcSq);
var sumXy = sumFunc(zip(xs,ys),funcSq2);
rV.slope = ((N * sumXy) - (sumX * sumY)) / (N * sumXx - (sumX*sumX));
rV.intercept = (sumY - rV.slope * sumX) / N;
rV.rSquared = Math.abs((rV.slope * (sumXy - (sumX * sumY) / N)) / (sumYy - ((sumY * sumY) / N)));
return true;
}
// sums arrays with optional function transformation
sumFunc = function(arr, func){
var total = 0;
$.each(arr, function(i,k){
if ($.isArray(k)){
if (func == null){
k = k[0] + k[1];
}else{
k = func(k[0],k[1]);
}
} else {
if (func != null){
k = func(k);
}
}
total += k;
});
return total;
}
// python style zip function
// to pair to array together
zip = function(arr1,arr2) {
var rV = [];
for(var i=0; i<arr1.length; i++){
rV.push([arr1[i],arr2[i]]);
}
return rV;
}
The lineFit function will return the rV object (by reference) with attributes of slope and intercept. After that you can add a line to Highcharts with good old fashioned y = slope * x + intercept and minX is the starting value for the regression line and maxX is the ending value.
{
name: 'Regression Line',
data: [[minX, reg.slope * minX + reg.intercept],
[maxX, reg.slope * maxX + reg.intercept]],
color: 'red',
marker:{enabled:false},
lineWidth: 5
}
Working fiddle here.
Based on ideas provided by the answer from Mark, I wrote some code to generate a custom fourth line, using the data from all three lines, and calculating the required value for each point.
My new code is as follows:
line1 = [
[Date.UTC(2014,0,16), 173.33],
[Date.UTC(2014,0,23), 163.33],
[Date.UTC(2014,0,30), 137.67],
[Date.UTC(2014,1,6), 176.33],
[Date.UTC(2014,1,13), 178.67],
[Date.UTC(2014,1,27), 167.33],
];
line2 = [
[Date.UTC(2014,0,11), 156.33],
[Date.UTC(2014,1,15), 167.67],
];
line3 = [
[Date.UTC(2014,0,1), 135],
[Date.UTC(2014,0,5), 146.33],
[Date.UTC(2014,0,27), 146.75],
[Date.UTC(2014,2,2), 168.75]
];
function average(array, index) {
sum = array[0][1];
for(var i = 1; i <= index; i++) {
sum += array[i][1];
}
value = sum / (index + 1);
return parseFloat(value.toFixed(2));
}
// Make a fourth line with all of the data points for the other
// three lines, sorted by date
all_lines = line1.concat(line2).concat(line3);
all_lines.sort(function(a, b) { return a[0] - b[0]});
// Calculate the value for each data point in the fourth line -
// the average of all the values before it
average_line = [];
for(var i = 0; i < all_lines.length; i++) {
average_line.push([all_lines[i][0], average(all_lines, i)])
}
$('#chart').highcharts({
chart: { type: 'spline' },
title: {
text: '',
},
xAxis: {
type: 'datetime'
},
yAxis: {
title: {
text: ''
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
borderWidth: 0
},
series: [{
name: 'Line 1',
data: line1,
color: 'purple'
},
{
name: 'Line 2',
data: line2,
color: 'green'
},
{
name: 'Line 3',
data: line3,
color: 'blue'
},
{
name: 'Average',
data: average_line,
color: 'red'
}]
});
The graph as it looks now (with one extra data point on the blue line) is:

Categories