The best way to pass rows of an SQL Database to Javascript? - c#

So, recently I've been looking into the HTML5 Indexeddb feature which is controlled with Javascript, and how to incorporate this with an SQL Server database to share and copy information across so that a version of the database is available offline.
The conundrum that I have is finding the best way to pass a set of objects from an SQL Server database through to the javascript of a browser, using C# to get the values from the database.
Though there are many little "hacks" that can be done (e.g Putting data into labels and then having the Javascript just pick it up document.getElementById("DataHere").value;) I was wondering if there were more efficient ways of passing the contents of a database across?
At the moment I have a method that generates an array of strings (all in the JSON format, using the JSON.Net package) as an example of what would be returned from the database.
public string[] GenerateEmployeeSample() {
List<Employee> listEmps = new List<Employee>();
string[] listJSON = new string[64];
string[] FirstNames = {"Alonso", "Alfred", "Angus", "Alfresco" };
string[] LastNames = {"Johnson","Williams", "Zelwegger", "Jones" };
string[] Departments = {"Finance", "Cleaning", "Pusher", "Stairs" };
string emailEnd = "#email.com";
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 4; k++) {
Employee tempEmp = new Employee();
tempEmp.FirstName = FirstNames[i];
tempEmp.LastName = LastNames[j];
tempEmp.Department = Departments[k];
tempEmp.Email = FirstNames[i] + "." + LastNames[j] + emailEnd;
listEmps.Add(tempEmp);
}
}
}
int count = 0;
foreach(Employee emp in listEmps){
string tempString = JsonConvert.SerializeObject(emp);
listJSON[count] = tempString;
count++;
}
return listJSON;
}
So now I have a set of data, how would I be able to pass this array through to Javascript?
I apologise if this is a trivial question but Javascript is still rather new to me, and after reading through quite a few examples that either didn't do what I wanted or just outright didn't work I am at a bit of a loss.
Thanks very much for reading!

To be more specific, you would make a resource (be it an aspx page or or a json file etc) that returns a JSON string when requested. You're already partway there with this step, because you've already got the code for serializing objects as JSON. A simple way of doing this would be to have a page that outputs the return value of this function using Response.Write(GenerateEmployeeSample());
Then you would use the XMLHttpRequest object to request this resource, and parse the returned JSON using javascript's JSON.parse, which returns a native javascript object. You may then do with this object as you please.

Related

How to turn a string into a 2d string array

as the title suggests, I am looking for guidance in how to turn a string (csvData) into a 2D string array by splitting it two times with ';' and ',' respectivly.
Currently I am at the stage where I am able to split it once into rows and turn it into an array, but I cannot figure out how to instead create a 2D array where the columns divided by ',' are also separate.
string[] Sep = csvData.Split(';').Select(csvData => csvData.Replace(" ","")).Where(csvData => !string.IsNullOrEmpty(csvData)).ToArray();
I have tried various things like :
string[,] Sep = csvData.Split(';',',').Select(csvData => csvData.Replace(" ","")).Where(csvData => !string.IsNullOrEmpty(csvData)).ToArray();
naivly thinking that c# would understand what I tried to achieve, but since I am here it's obvious that I got the error that "cannot implicitly convert type string[] to string [*,*]"
Note that I have not coded for a while, so if my thinking is completely wrong and you do not understand what I am trying to convey with this question, I apologize in advance.
Thanks!
In a strongly-typed language like C#, the compiler makes no assumptions about what you intend to do with your data. You must make your intent explicit through your code. Something like this should work:
string csvData = "A,B;C,D";
string[][] sep = csvData.Split(';') // Returns string[] {"A,B","C,D"}
.Select(str => str.Split(',')) // Returns IEnumerable<string[]> {{"A","B"},{"C","D"}}
.ToArray(); // Returns string[][] {{"A","B"},{"C","D"}}
Rows are separated by semicolon, columns by comma?
Splitting by ';' gives you an array of rows. Split a row by ',' gives you an array of values.
If your data has a consistent schema, as in each csv you process has the same columns, you could define a class to represent the entity to make the data easier to with with.
Let's say it's customer data:
John,Smith,8675309,johnsmith#gmail.com;
You could make a class with those properties:
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
}
Then:
var rows = csvdata.Split(';');
List<Customer> customers = new();
foreach(var row in rows)
{
var customer = row.Split(',');
customers.Add(new()
{
FirstName = row[0],
LastName = row[1],
Phone = row[2],
Email = row[3]
});
}
Now you have a list of customers to do whatever it is you do with customers.
Here is an answer to present a few alternative ideas and things you can do with C# - more for educational/academic purposes than anything else. These days to consume a CSV we'd use a CSV library
If your data is definitely regularly formed you can get away with just one Split. The following code splits on either char to make one long array. It then stands to reason that every 4 elements is a new customer, the data of the customer being given by n+0, n+1, n+2 and n+3. Because we know how many data items we will consume, dividing it by 4 gives us the number of customers so we can presize our 2D array
var bits = data.Split(';',',');
var twoD = new string[bits.Length/4,4];
for(int x = 0; x < bits.Length; x+=4){
twoD[x/4,0] = bits[x+0];
twoD[x/4,1] = bits[x+1];
twoD[x/4,2] = bits[x+2];
twoD[x/4,3] = bits[x+3];
}
I don't think I'd use 2D arrays though - and I commend the other answer advising to create a class to hold the related data; you can use this same technique
var custs = new List<Customer>();
for(int x = 0; x < bits.Length;){
custs.Add(new()
{
FirstName = bits[x++],
LastName = bits[x++],
Phone = bits[x++],
Email = bits[x++]
});
}
Here we aren't incrementing x in the loop header; every time a bit of info is assigned x is bumped up by 1 in the loop body. We could have kept the same approach as before, jumping it by 4 - just demoing another approach that lends itself well here.
I mentioned that these days we probably wouldn't really read a csv manually and split ourselves - what if the data contains a comma, or a semicolon - it wrecks the file structure
There are a boatload of libraries that read CSV files, CsvHelper is a popular one, and you'd use it like:
using var reader = new StreamReader("path\\to\\file.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture)
var custs = csv.GetRecords<Customer>().ToList();
...
Your file would have a header line with column names that match your property names in c#. If it doesn't then you can use attributes on the properties to tell CsvH what column should be mapped to what property - https://joshclose.github.io/CsvHelper/getting-started/
Here's the simplest way I know to produce a 2d array by splitting a string.
string csvData = "A,B,C;D,E,F,G";
var temporary =
csvData
.Split(';')
.SelectMany((xs, i) => xs.Split(',').Select((x, j) => new { x, i, j }))
.ToArray();
int max_i = temporary.Max(x => x.i);
int max_j = temporary.Max(x => x.j);
string[,] array = new string[max_i + 1, max_j + 1];
foreach (var t in temporary)
{
array[t.i, t.j] = t.x;
}
I purposely chose csvData to be missing a value.
temporary is this:
And the final array is this:

Table of strings in C#

Is there a way of creating a table with each cell containing a string in C# ?
The closest thing I found is multidimensional arrays string[,] names;, but it seems like its length needs to be defined which is a problem to me.
Here is what my code looks like :
string[] namePost;
int[] numbPage;
string post="";
string newPost;
int i=0;
int j=0;
foreach (var line in File.ReadLines(path).Where(line => regex1.Match(line).Success))
{
newPost = regex1.Match(line).Groups[1].Value;
if (String.Compare(newPost, post) == 0)
{
j = j + 1;
}
else
{
namePost[i] = post;
numbPage[i] = j;
post = newPost;
j = 1;
i = i + 1;
}
}
Each instance of the for writes the name of the new "post" in a cell of namePost. In the end, the namePost table stores the name of all the posts that are different from one another.
What is the best way to achieve that ?
If you are simply trying to store the posts, you can use the List class from the System.Collections.Generic namespace:
using System.Collections.Generic;
List<String> namePost = new List<String>();
Then, instead of namePost[i] = post;, use
namePost.Add(post);
DataTable
https://msdn.microsoft.com/en-us/library/system.data.datatable(v=vs.110).aspx
Use this, no need to define length at all.
Useful guide and examples:
http://www.dotnetperls.com/datatable
You can just use a
var table = new List<List<string>>();
This would give you a dynamic 2D table of strings.
This will give you all your unique posts. If you want the result as a list you can just do a
.ToList ()
with the result.
static IEnumerable<string> AllPosts(Regex regex, string filePath)
{
return File.ReadLines (filePath)
.Where (line => regex.Match (line).Success)
.Select (line => regex.Match (line).Groups [1].Value)
.Distinct ();
}

ViewBag on a Javascript code mvc 4

I am having a trouble doing this: I have a ViewBag that contains the list of documents Id's and the response time for each one, created like this:
Controller:
ViewBag.Documents= (from n in db.DocumentType
select new {a = n.DocumentTypeId, b=n.ResponseTime});
Based on the document that the user selected on a dropdownlist, the javascript code calculates the real duration of processing the document (duration).
DocumentId(int) Response(int)
1 2 days
2 3 days
3 1 day
The complete javascript code calculates the final date based on response time without weekends and holidays, but the only part that it is not working is the following part of JavaScript code:
JavaScript at the view:
function CalculateTime(documenttype) {
var duration= 0;
var jScriptArray= new Array();
var array = '#Html.Raw(Json.Encode(ViewBag.Documents))';
for (var i = 0; i < array.length; i++) {
for (var j = 0; j < 2; j++) {
jScriptArray [i][j] = array [i][j];
}
}
for (i = 0; i < jScriptArray.length; i++) {
if (documenttype== jScriptArray [i][0]) duration= jScriptArray [i][1];
}
return duration;
}
I already have the correct documenttype variable based on a dropdownlist but this script is not working. I think is the way I want to use the ViewBag, but I am not pretty sure. What I need to know is: what is wrong with my code or another way to do this.
Thanks in advance
Just remove quotes
var array = #Html.Raw(Json.Encode(ViewBag.Documents));
Just remove the quotes(') and you have to Newtonsoft.Json for this.
var array = #Newtonsoft.Json.JsonConvert.SerializeObject(ViewBag.Documents);
This will work.
A friend of mine helped me solve my problem. Some of you told me to use Json, so we did this:
Controller:
using System.Web.Script.Serialization;
using Newtonsoft.Json;
...
var timeList= db.DocumentType.ToList();
ViewBag.ResponseTimes= new JavaScriptSerializer().Serialize(
JsonConvert.SerializeObject(new
{
times= (from obj in timeList
select new { DocumentTypeId= obj.DocumentTypeId, ResponseTime= obj.ResponseTime})
}, Formatting.None));
JavaScritp at the View:
function CalculateTime(documenttype) {
var duration = 0;
var jScriptArray = new Array();
var array = $.parseJSON(#Html.Raw(ViewBag.ResponseTimes));
for (i = 0; i < array.times.length; i++) {
if (parseInt(documenttype) == array.times[i].DocumentTypeId) {
duration= array.times[i].ResponseTimes;
}
}
return duration;
}
Thanks!
PS: I don't know which of both answers give the acceptance cause we used part of both....

Using Accord.Net's Codification Object to Codify second data set

I am trying to figure out how to use the Accord.Net Framework to make a bayesian prediction using the machine learning NaiveBayes class. I have followed the example code listed in the documentation and have been able to create the model from the example.
What I can't figure out is how to make a prediction based on that model.
The way the Accord.Net framework works is that it translates a table of strings into numeric symolic representation of those strings using a class called Codification. Here is how I create inputs and outputs DataTable to train the model (90% of this code is straight from the example):
var dt = new DataTable("Categorizer");
dt.Columns.Add("Word");
dt.Columns.Add("Category");
foreach (string category in categories)
{
rep.LoadTrainingDataForCategory(category,dt);
}
var codebook = new Codification(dt);
DataTable symbols = codebook.Apply(dt);
double[][] inputs = symbols.ToArray("Word");
int[] outputs = symbols.ToIntArray("Category").GetColumn(0);
IUnivariateDistribution[] priors = {new GeneralDiscreteDistribution(codebook["Word"].Symbols)};
int inputCount = 1;
int classCount = codebook["Category"].Symbols;
var target = new NaiveBayes<IUnivariateDistribution>(classCount, inputCount, priors);
target.Estimate(inputs, outputs);
And this all works successfully. Now, I have new input that I want to test against the trained data model I just built. So I try to do this:
var testDt = new DataTable("Test Data");
testDt.Columns.Add("Word");
foreach (string token in tokens)
{
testDt.Rows.Add(token);
}
DataTable testDataSymbols = codebook.Apply(testDt);
double[] testData = testDataSymbols.ToArray("Word").GetColumn(0);
double logLikelihood = 0;
double[] responses;
int cat = target.Compute(testData, out logLikelihood, out responses);
Notice that I am using the same codebook object that I was using previously when I built the model. I want the data to be codified using the same codebook as the original model, otherwise the same word might be encoded with two completely different values (the word "bob" in the original model might correspond to the number 23 and in the new model, the number 43... no way that would work.)
However, I am getting a NullReferenceException error on this line:
DataTable testDataSymbols = codebook.Apply(testDt);
Here is the error:
System.NullReferenceException: Object reference not set to an instance of an object.
at Accord.Statistics.Filters.Codification.ProcessFilter(DataTable data)
at Accord.Statistics.Filters.BaseFilter`1.Apply(DataTable data)
at Agent.Business.BayesianClassifier.Categorize(String[] categories, String testText)
The objects I am passing in are all not null, so this must be something happening deeper in the code. But I am not sure what.
Thanks for any help. And if anyone knows of an example where a prediction is actually made from the bayesian example for Accord.Net, I would be much obliged if you shared it.
Sorry about the lack of documentation on the final part. In order to obtain the same integer codification for a new word, you could use the Translate method of the codebook:
// Compute the result for a sunny, cool, humid and windy day:
double[] input = codebook.Translate("Sunny", "Cool", "High", "Strong").ToDouble();
int answer = target.Compute(input);
string result = codebook.Translate("PlayTennis", answer); // result should be "no"
but it should also have been possible to call codebook.Apply to apply the same transformation to a new dataset. If you feel this is a bug, would you like to fill a bug report in the issue tracker?

Unable to Save List<dynamic> using Rob Conery's Massive

I'm using Rob Conery's Massive to connect to my database, but I don't seem to be able to be able to save a list of dynamic objects to the database. I thought this was supported though.
Here's the code I am attempting to use:
int numberOfChildren = int.Parse(Request.Form["numberOfChildren"]);
List<dynamic> children = new List<dynamic>();
for(int i = 1; i <= numberOfChildren; i++) {
dynamic child = new ExpandoObject();
child.FamilyID = familyId;
child.Type = "CHILD";
child.LastName = Request.Form[i + "-childLastName"];
child.FirstName = Request.Form[i + "-childFirstName"];
child.SendSmsAlerts = false;
child.Gender = Request.Form[i + "-childGender"];
child.Birthdate = Request.Form[i + "-childBirthdate"];
children.Add(child);
}
var people = new People();
people.Save(children);
I get a "Parameter count mismatch." error on line 78 of Massive.cs
Everything works fine if i only pass in a single dynamic object at a time, the error is only raised when I attempt to pass in the list. Based on the documentation on GitHub I thought this was supported and it would save all the children in one transaction.
Save takes an params array not a list.
people.Save(children.ToArray());

Categories