I'm working on a web application. I found this interesting https://github.com/mattosaurus/ChartJSCore. to use charts in my application.
The charts are working successfully in most pages. But in one page i have the following idea:
I have 3 properties (Appropriate, Inappropriate, NoInteraction) in my model all with type (int) and i need to keep it as integer to manipulate other functionalities in the application. Each property will be represented as one series in the chart and it should be list or array of 15 integer always.
Here is my properties in Session Model:
public int DayNumber { get; set; }
public int Appropriate { get; set; }
public int NotAppropriate { get; set; }
public int NoInteraction { get; set; }
Here is my Controller:
public IActionResult Details()
{
var result = _db.Session.ToList();
//I want this appropriateLine to be passed to GenerateLineChart method but whenever i tried i came up with an error of converting types.
var AppropriateLine = result.Select(x => x.Appropriate).ToList();
Chart lineChart = GenerateLineChart();
ViewData["LineChart"] = lineChart;
return View();
}
private static Chart GenerateLineChart()
{
Chart chart = new Chart();
chart.Type = Enums.ChartType.Line;
ChartJSCore.Models.Data data = new ChartJSCore.Models.Data();
data.Labels = new List<string>() { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
LineDataset AppropriateDataset = new LineDataset()
{
Label = "Appropriate Data Line",
Data = new List<double?>() { 2, 6, 2, 6, 2, 6 }, //Here i want this to be filled with data from AppropriateLine variable, it works for the fixed value only
Fill = "false",
LineTension = 0.1,
BackgroundColor = ChartColor.FromHexString("#FF6384"),
BorderColor = ChartColor.FromHexString("#FF6384"),
BorderCapStyle = "butt",
BorderDash = new List<int> { },
BorderDashOffset = 0.0,
BorderJoinStyle = "miter",
PointBorderColor = new List<ChartColor>() { ChartColor.FromHexString("#FF6384"), },
PointBackgroundColor = new List<ChartColor>() { ChartColor.FromHexString("#fff") },
PointBorderWidth = new List<int> { 1 },
PointHoverRadius = new List<int> { 5 },
PointHoverBackgroundColor = new List<ChartColor>() { ChartColor.FromHexString("#FF6384"), },
PointHoverBorderColor = new List<ChartColor>() { ChartColor.FromHexString("#FF6384"), },
PointHoverBorderWidth = new List<int> { 2 },
PointRadius = new List<int> { 1 },
PointHitRadius = new List<int> { 10 },
SpanGaps = false
};
data.Datasets = new List<Dataset>();
data.Datasets.Add(AppropriateDataset);
Options options = new Options()
{
Scales = new Scales()
};
Scales scales = new Scales()
{
YAxes = new List<Scale>()
{
new CartesianScale()
}
};
CartesianScale yAxes = new CartesianScale()
{
Ticks = new Tick()
};
Tick tick = new Tick()
{
Callback = "function(value, index, values) {return '' + value;}"
};
yAxes.Ticks = tick;
scales.YAxes = new List<Scale>() { yAxes };
options.Scales = scales;
chart.Options = options;
chart.Data = data;
return chart;
}
How can i implement this "Explicit Casting" of appropriateLine variable which is (int) before passing it to GenerateLineChart().
Note that i don't want to change Model properties type since many functions depend on it. Also, I cannot change Data type from List<double?> since many other problems solved by adding this.
I tried many casting solutions but none of them works for me such as:
(List<double?>)result.Select(x => x.Appropriate);
private static Chart GenerateLineChart((List<double?>)AppropriateLine)
I've read about "Convert all" method but not worked.
Any help is much appreciated,
Thanks in advance.
Based on what you mention, it seems that the data passed to GenerateLineChart will only be used for UI purposes. That is, this method can get an appropriate copy of the original data. If this is the case, then, the solution you attempted with
(List<double?>)result.Select(x => x.Appropriate)
was very close, but the cast needs to be done inside Select, i.e.
result.Select(x => (double?)x.Appropriate)
Here is a sketch of your code with that change
public IActionResult Details()
{
var result = _db.Session.ToList();
var AppropriateLine = result.Select(x => (double?)x.Appropriate).ToList();
var lineChart = GenerateLineChart(AppropriateLine);
// Rest or your code
}
private static Chart GenerateLineChart(IEnumerable<double?> data)
{
// Your code as is here ....
LineDataset AppropriateDataset = new LineDataset()
{
Data = data,
// Rest of your code
}
// ....
}
maybe you can check at the moment that you need the list if the number is double or int bool isInt = d == (int)d; and parse to use it only for that time on an aux.
Related
How should i bind the values without using index no. as above, how can use forLoop here if possible. in query i gor 14 rows and 4 columns.
public class SLRInvestmentPrev
{
[DbCol("BOOK_VALUE")]
public double BOOK_VALUE { get; set; }
[DbCol("INSTRUMENT_ID")]
public int instrument_id { get; set; }
}
Public void Compute()
{
var slrinvestmentPrev = Database.BindList<SLRInvestmentPrev>(Transaction, #"Query here");
View["BOOK_VALUE_HTM0"] = slrinvestmentPrev[0].BOOK_VALUE;
View["BOOK_VALUE_HTM1"] = slrinvestmentPrev[1].BOOK_VALUE;
View["BOOK_VALUE_HTM2"] = slrinvestmentPrev[2].BOOK_VALUE;
View["BOOK_VALUE_HTM3"] = slrinvestmentPrev[3].BOOK_VALUE;
View["BOOK_VALUE_HFT1"] = slrinvestmentPrev[4].BOOK_VALUE;
View["BOOK_VALUE_HFT2"] = slrinvestmentPrev[5].BOOK_VALUE;
View["BOOK_VALUE_HFT3"] = slrinvestmentPrev[6].BOOK_VALUE;
View["BOOK_VALUE_HFT4"] = slrinvestmentPrev[7].BOOK_VALUE;
View["BOOK_VALUE_HFT5"] = slrinvestmentPrev[8].BOOK_VALUE;
View["BOOK_VALUE_AFS1"] = slrinvestmentPrev[9].BOOK_VALUE;
View["BOOK_VALUE_AFS2"] = slrinvestmentPrev[10].BOOK_VALUE;
View["BOOK_VALUE_AFS3"] = slrinvestmentPrev[11].BOOK_VALUE;
View["BOOK_VALUE_AFS4"] = slrinvestmentPrev[12].BOOK_VALUE;
View["BOOK_VALUE_AFS5"] = slrinvestmentPrev[13].BOOK_VALUE;
}
given your books are HTM0 to AFS5 you could do something like
List<String> booklist = new List<string>(new String[] { "BOOK_VALUE_HTM0", "BOOK_VALUE_HTM1", "BOOK_VALUE_HTM2",<.....> "BOOK_VALUE_AFS5" } ); // or populate from some other means
int index = 0;
foreach (String sbook in booklist)
{
View[sbook] = slrinvestmentPrev[index].BOOK_VALUE;
index++
}
how to bind that right side index values to different variables using
for loop or is there any way to make code short and being error free
if suppose to be rows no are less than hard coded index values?
So you need a way to bind your names to the indexes, one way it to use a Dictionary:
var nameIndexes = new Dictionary<int, string>()
{
{ 0, "BOOK_VALUE_HTM0" }, { 1, "BOOK_VALUE_HTM1" }, { 2, "BOOK_VALUE_HTM2" }, { 3, "BOOK_VALUE_HTM3" },
{ 4, "BOOK_VALUE_HFT1" }, { 5, "BOOK_VALUE_HFT2" }, { 6, "BOOK_VALUE_HFT3" }, { 7, "BOOK_VALUE_HFT4" }, { 8, "BOOK_VALUE_HFT5" },
{ 9, "BOOK_VALUE_AFS1" }, { 10, "BOOK_VALUE_AFS2" }, { 11, "BOOK_VALUE_AFS3" }, { 12, "BOOK_VALUE_AFS4" }, { 13, "BOOK_VALUE_AFS5" }
};
for(int i = 0; i < slrinvestmentPrev.Count; i++)
{
View[nameIndexes[i]] = slrinvestmentPrev[i].BOOK_VALUE;
}
If the index always starts with 0 and has no gaps you could also use a List<string> or string[].
I am programming in silverlight (c# .net)
lets say I have a list of type "data"
public class data
{
public string QUOTE_ID { get; set; }
public string EVENT_ACTION_CD { get; set; }
public string CUSTOMER_NAME { get; set; }
public string ADAPTIV_CODE { get; set; }
}
the problem is some of the data comes from 1 database and the other data comes from another, so right now i get the data in 2 steps - so i have something like this (using random numbers):
input1 = new List<data> //data return from database 1
//(the data is actually returned as a datable which i convert to a list
//to put to a datagrid, but the end result is shown below)
{
new data { QUOTE_ID = "1", EVENT_ACTION_CD = "2"},
new Project { QUOTE_ID = "2", EVENT_ACTION_CD = "4"},
new Project { QUOTE_ID = "3", EVENT_ACTION_CD = "5"}
};
input2 = new List<data> //data return from database 2
{
new data { QUOTE_ID = "1", CUSTOMER_NAME = "2", ADAPTIV_CODE ="5"},
new Project { QUOTE_ID = "2", CUSTOMER_NAME = "4", ADAPTIV_CODE = "5"},
new Project { QUOTE_ID = "3", CUSTOMER_NAME = "5", ADAPTIV_CODE = "7"}
};
so i should have 2 lists like
input1:
(1, 2, null, null
2, 4, null, null
3, 5, null, null)
and
input2:
(1, null, 2, 5
2, null, 4, 5
3. null, 5, 7)
how do i join them together to form one input list to become
(1, 2, 2, 5
2, 4, 4, 5
3, 5, 5, 7)
Use linq with a join operator.
See http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
var resultList = (from item in input1
join item2 in input2 on item2.QUOTE_ID equals input2.QUOTE_ID
let item.CUSTOMER_NAME = item2.CUSTOMER_NAME
let item.ADAPTIV_CODE = item2.ADAPTIV_CODE
select item).ToList();
A normal for loop would work for you:
for(int i = 0; i < input1.Count; i++){
if(input1[i].QUOTE_ID == null) input1[i].QUOTE_ID = input2[i].QUOTE_ID;
if(input1[i].EVENT_ACTION_CD == null) input1[i].EVENT_ACTION_CD = input2[i].EVENT_ACTION_CD;
if(input1[i].CUSTOMER_NAME == null) input1[i].CUSTOMER_NAME = input2[i].CUSTOMER_NAME;
if(input1[i].ADAPTIV_CODE == null) input1[i].ADAPTIV_CODE = input2[i].ADAPTIV_CODE;
}
The result will be saved into the input1. The code also supposes input1 and input2 have the same Count.
var input3 = input1.Join(
input2,
d1 => d1.QUOTE_ID,
d2 => d2.QUOTE_ID,
(d1, d2) => new data() {
QUOTE_ID = d1.QUOTE_ID,
EVENT_ACTION_CD = d1.EVENT_ACTION_CD,
CUSTOMER_NAME = d2.CUSTOMER_NAME,
ADAPTIV_CODE = d2.ADAPTIV_CODE
}
);
I am trying to populate a chart with data from my database. I'm using Entity framework and fairly new to asp.net.
What I'm trying to do is populate the chart from my linq query.
var totals = from s in db.ClassInstanceDetails.Include("ClassInstance")
where s.ClassInstance.ClassID == 2
group s by s.ClassInstance.Date into grouped
select new
{
CIDate = grouped.Key,
TotalStudentsInClass = grouped.Count(s => s.Attendance)
};
The linq query works fine, it counts all the students in a classinstance, groups them and counts them. My problem is how do extract the data and put it into the chart. When I debug I can see that the totals variable is
{System.Data.Objects.ObjectQuery<<>f__AnonymousType0<System.DateTime,int>>}
and I can see the results view held by totals as :
{CIDate = {04/09/2012}, TotalStudentsInClass = 5}
{CIDate = {05/09/2012}, TotalStudentsInClass = 7}
{CIDate = {06/09/2012}, TotalStudentsInClass = 14}
Which is great as the query works, it finds how many pupils attended a particular class instance. I'm trying to put this data into a line graph using highcharts. I've tried to separate the linq results into 2 arrays one array containing the dates and another array containing the TotalStudentsInClass value but having no luck as the types are different??? Is this even the right way to do this?
The example I found online pulls from an array I think
.SetSeries(new[]
{
new Series { Name = "Tokyo", Data = new Data(ChartsData.TokioData) },
new Series { Name = "New York", Data = new Data(ChartsData.NewYorkData) },
new Series { Name = "Berlin", Data = new Data(ChartsData.BerlinData) },
new Series { Name = "London", Data = new Data(ChartsData.LondonData) }
}
with the data coming from an object,
public static object[] TokioData = new object[] { 7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6 };
I've tried to run my linq query into an object but that throws me more errors than I know what to do with!!
Any help would be GREATLY appreciated!!
Chart
public static Highcharts TimeSeriesZoomable(Series[] Series, Number MinRange, Number PointInterval, DateTime PointStartDate, AxisTypes XAxisType = AxisTypes.Datetime, string Title = "", string SubTitle = "", string XAxisTitle = "", string YAxisTitle = "", string ToolTipFormat = "", string YAxisLabel = "")
{
Highcharts chart = new Highcharts("chart")
.SetOptions(new GlobalOptions { Global = new Global { UseUTC = false } })
.InitChart(new Chart { ZoomType = ZoomTypes.X, SpacingRight = 20, DefaultSeriesType = ChartTypes.Area, Height = 300, BorderRadius = 0 })
.SetTitle(new Title { Text = Title })
.SetSubtitle(new Subtitle { Text = SubTitle })
.SetXAxis(new XAxis
{
Type = XAxisType,
MinRange = MinRange,
Title = new XAxisTitle { Text = XAxisTitle }
})
.SetYAxis(new YAxis
{
Title = new YAxisTitle { Text = YAxisTitle },
Min = 0.6,
StartOnTick = false,
EndOnTick = false,
Labels = new YAxisLabels
{
Formatter = #"function() { return this.value +' " + YAxisLabel + "';}"
}
})
.SetTooltip(new Tooltip { Shared = true/*, Formatter = #"function() { return ''+ this.x +' - '+ this.y +' " + ToolTipFormat + "'; }" */})
.SetLegend(new Legend { Enabled = true, VerticalAlign = VerticalAligns.Top })
.SetPlotOptions(new PlotOptions
{
Line = new PlotOptionsLine
{
LineWidth = 3,
Marker = new PlotOptionsLineMarker
{
Enabled = false,
States = new PlotOptionsLineMarkerStates
{
Hover = new PlotOptionsLineMarkerStatesHover
{
Enabled = true,
Radius = 5
}
}
},
Shadow = false,
States = new PlotOptionsLineStates { Hover = new PlotOptionsLineStatesHover { LineWidth = 3 } },
PointInterval = PointInterval,
PointStart = new PointStart(PointStartDate)
},
Spline = new PlotOptionsSpline
{
LineWidth = 3,
Marker = new PlotOptionsSplineMarker
{
Enabled = false,
States = new PlotOptionsSplineMarkerStates
{
Hover = new PlotOptionsSplineMarkerStatesHover
{
Enabled = true,
Radius = 5
}
}
},
Shadow = false,
States = new PlotOptionsSplineStates { Hover = new PlotOptionsSplineStatesHover { LineWidth = 3 } },
PointInterval = PointInterval,
PointStart = new PointStart(PointStartDate)
},
Area = new PlotOptionsArea
{
//FillColor = new BackColorOrGradient(new Gradient
//{
// LinearGradient = new[] { 0, 0, 0, 300 },
// Stops = new object[,] { { 0, "rgb(116, 116, 116)" }, { 1, Color.Gold } }
//}),
LineWidth = 1,
Marker = new PlotOptionsAreaMarker
{
Enabled = false,
States = new PlotOptionsAreaMarkerStates
{
Hover = new PlotOptionsAreaMarkerStatesHover
{
Enabled = true,
Radius = 5
}
}
},
Shadow = false,
States = new PlotOptionsAreaStates { Hover = new PlotOptionsAreaStatesHover { LineWidth = 1 } },
PointInterval = PointInterval,
PointStart = new PointStart(PointStartDate)
}
})
.SetSeries(Series);
return chart;
}
Chart Data
public static Series GetTimeSeriesData(IQueryable<YourModel> model, ChartTypes ChartType)
{
List<Series> Series = new List<Series>();
var chartSeries = model.GroupBy(x => x.Name)
.Select(g => new
{
Name = g.Key,
Data = g.Select(x => x.Value).ToArray()
}).ToArray();
foreach (var item in chartSeries)
{
object[] data = item.Data.Cast<object>().ToArray();
Series localSeries = new Series { Name = item.Name, Data = new Data(data), Type = ChartType };
Series.Add(localSeries);
}
return Series;
}
Usage
IQueryable<YourModel> model;
ChartData chartData = new ChartData();
Highcharts chart = new HighChart("chart_time_series");
try
{
model = db.ClassInstanceDetails.AsQueryable();
chartData = GetTimeSeriesData(model, ChartTypes.Line);
chart = TimeSeriesZoomable(chartData.ToArray(), another_options);
}
catch (Exception e)
{
}
And full examples of charts : http://dotnethighcharts.codeplex.com/releases/view/85324
Are you using Highcharts.Net? If so, I'm not sure how much help I can be (I have only done it manually, creating my own objects and converting to JSON etc, which I found to give me complete control, albeit with more effort)
Anyway, this usually largely depends on how your X-axis needs to behave... it appears like it would just be discrete datetime values, (not automatically sequential), and so your object array probably needs to be composed of X- and Y-values, rather than just the Y-values, as it appears you have there.
This next part really depends on how your implementation works, so please excuse the pseudo-pseudo-code...
You either need a 2D array:
eg: data = [[1,1], [2,5], [3,4]...]
or something more concrete: I use a class which has an X and a Y property (amongst other things), but you could try anonymous types perhaps?
eg [{x=1, y=1}, {x=2, y=5}, {x=3, y=4}...] etc
Does this help at all?
Note: you will probably want to read up a bit on how to convert your datetime values for your x-axis - I had to calculate ticks from Epoch etc
I do something like this and the value in the collection doesn't change
[Test]
public void EnumerableTest()
{
var source = GetFoos();
source.First().One = "hello";
Assert.AreEqual(source.First().One, "hello");
//it fails
}
//I actually return this from a repository
public IEnumerable<Foo> GetFoos()
{
yield return new Foo() {One = "1", Two = "2", Three = true};
yield return new Foo() {One = "1", Two = "2", Three = true};
yield return new Foo() {One = "1", Two = "2", Three = true};
}
That is because you create new instances each time you enumerate over GetFoos.
If you change the var source = GetFoos(); into var source = GetFoos().ToList();, the list is read immediately (and in full). Then you should be able to change the values.
Don't forget to store the changed values or else they revert the next time you read them.
It is because of your use of yield return.
You could write instead:
public IEnumerable<Foo> GetFoos()
{
return new List<Foo>
{
new Foo { One = "1", Two = "2", Three = true },
new Foo { One = "1", Two = "2", Three = true },
new Foo { One = "1", Two = "2", Three = true },
};
}
When you call First() a new Enumerator is created.
So GetFoos() is called again and return a new object.
What does such an expression mean?
obj.DataSource = new[]
{
new {Text = "Silverlight", Count = 10, Link="/Tags/Silverlight" },
new {Text = "IIS 7", Count = 11, Link="http://iis.net" },
new {Text = "IE 8", Count = 12, Link="/Tags/IE8" },
new {Text = "C#", Count = 13, Link="/Tags/C#" },
new {Text = "Azure", Count = 13, Link="?Tag=Azure" }
};
Especially these lines: new {Text = "IIS 7"... }
How can I create an array like this manually to suit this DataSource.
First, let's reformat that code a little:
obj.DataSource = new[]
{
new { Text = "Silverlight", Count = 10, Link = "/Tags/Silverlight" },
new { Text = "IIS 7", Count = 11, Link = "http://iis.net" },
new { Text = "IE 8", Count = 12, Link = "/Tags/IE8" },
new { Text = "C#", Count = 13, Link = "/Tags/C#" },
new { Text = "Azure", Count = 13, Link = "?Tag=Azure" }
};
This does not look like a multi-dimensional array, but rather like an array of 5 objects. These objects inside the array are of an anonymous type, created and initialized using new { ... }.
Concerning your question how you can manually create such an array to suit the data source: you seem to be doing exactly that with the above code.
That's not a multidimensional array. That's an array of objects which have been created using object initializers with anonymous types.
Looks like an array of anonymous types.
http://msdn.microsoft.com/en-us/library/bb397696.aspx
Just to add: Anonymous types are converted by the compiler to a real object. So the code will be changed to something equivalent of this (MUCH simplified, only to show that the compiler will create an actual class):
internal sealed class AnonymousClass1
{
internal string Text { get; set; }
internal int Count { get; set; }
internal string Link { get; set; }
}
And your code will then be changed to:
obj.DataSource = new AnonymousClass1[]
{
new AnonymousClass1 {Text = "Silverlight", Count = 10, Link="/Tags/Silverlight" },
new AnonymousClass1 {Text = "IIS 7", Count = 11, Link="http://iis.net" },
new AnonymousClass1 {Text = "IE 8", Count = 12, Link="/Tags/IE8" },
new AnonymousClass1 {Text = "C#", Count = 13, Link="/Tags/C#" },
new AnonymousClass1 {Text = "Azure", Count = 13, Link="?Tag=Azure" }
};
In one of my programs, I have code like this (simplified!):
var myObjects = new []{
new { Id = Guid.NewGuid(), Title = "Some Title", Description = string.Empty },
new { Id = Guid.NewGuid(), Title = "Another Title", Description = string.Empty },
new { Id = Guid.NewGuid(), Title = "Number 3", Description = "Better than No2, but not much" }
}
foreach(var myObject in myObjects) {
DoSomeThingWith(myObject.Title);
}
This works because it is just another class (I even get IntelliSense) behind the scenes. The benefit is obviously that I just saved myself from creating a class for this object. In my example, all objects need to look the same as well. (Obviously, doing this for any public members would be a bad idea as the compiler might change the name of the anonymous class if you add/remove some)
It's making a new object array, containing a group of anonymous objects.
new {Text = "Azure", Count = 13, Link="?Tag=Azure" }
is not creating a hash like similar syntax in say php would, but rather real a real object with the properties Test, Count, and Link set.
A good primer for anonymous objects can be found here
You should be able to use the same syntax to create new structures like this, the property values do not have to be constants:
string text = "Azure";
int count = 13;
string link = "?Tag=Azure";
new {Text = text, Count = count, Link=link }
To return such an array from a function I used:
public object GetAnonymousArray()
{
var tbl = new List<object>();
while (___)
{
//fill array in cycle
tbl.Add(new {Text = someString, Count = someInt, Link = link});
}
return tbl;
}