Unable to produce a chart using linq in csharp - c#

I am coding in Microsoft Visual Studio 12 in ASP.net using c# and a noob at it.
This is what csv file looks like
ID, Engine Type, Car
111,vtec, 1
131,vtec, 1
157,boxer,1
148,boxer,1
167,vtec,1
158,,0
107,,0
ID should be a generic autonumber, Engine Type should be a type string and I don't know what Car should because it consists off 1's and 0's representing Boolean. This means that 1-customer has a car and 0 means that customer doesn't have a car.
This is how i create a list out of it.
var testingobject = (
from line in File.ReadAllLines("testing.csv").Skip(0)
let parts = line.Split(',')
select new
{
ID = parts[0],
Engine_Type = parts[1],
Car = parts[2] //should i put int32.parse(parts[2]) here
).ToList();
I create a simple array which consists of ID,Engine Type,Car convert it to a list using ToList() and then bind it to a dropdownlist, using this code:
string[] testingddl = new string[] { "ID", "Engine Type", "Car" };
List<String> mytestinglist = testingddl.ToList();
var mytestin = from m in mytestinglist
select m;
DropDownList1.DataSource = mytestin.AsEnumerable();
DropDownList1.DataTextField = "";
DropDownList1.DataValueField = "";
DropDownList1.DataBind();
User selects Car and should give me the a chart that has engine type in x-axis and total on y-axis.
The Problem: The column consists of 1's and 0's meaning whether the customer has a car (1) or doesnt (0).
I would like to view how many users have different types of engines types and bind it to a, say a column chart. So the data should display on the x-axis, engine type and y-axis should have the total. according to the data, there are 3 vtecs and 2 boxers. So vtecs represent a column with 3 total and boxers with 2.
I use this linq query and the following chart binding code.
if (tempval == "Car")// this is the current selected dropdown list value
{
var myfavitems = testingobject.Where(a => a.Car == "1").ToList();
foreach (var t in myfavitems.GroupBy(a => a.Engine_Type))
{
Series Series1 = new Series();
Chart1.Series.Add(Series1);
Chart1.Series[1].Points.AddXY(t.Key.ToString(), t.Count().ToString()).ToString();
Chart1.DataSource = myfavitems.AsEnumerable();
Chart1.Series[1].XValueMember = "Group Equipment Type";
Chart1.Series[1].YValueMembers = "Software";
Chart1.DataBind();
// ...??
The error comes at the point where I am reading my columns from the csv files. The error says Format exception was unhandled by user code: Input string was not in a correct format
select new
{
ID = parts[0],
Engine_Type = parts[1],
Car = Int32.Parse(parts[2])
}
I dont know what is wrong with that statement. Could someone please help me out.

You will want to have a class like this:
public class CarClass {
public int Id { get; set; }
public string Engine { get; set; }
public int Car { get; set; }
}
And then do something like:
var car_collection =
from line in File.ReadAllLines("your_csv_name.csv").Skip(1)
let parts = line.Split(',')
select new CarClass()
{
Id = Int32.Parse(parts[0]),
Engine = parts[1],
Car = Int32.Parse(parts[2])
};
var listy = car_collection.ToList();
Otherwise, notice the '{ }' and the '( )' , because yours don't match:
var car_collection =
(from line in File.ReadAllLines("your_csv_name.csv").Skip(1)
let parts = line.Split(',')
select new CarClass()
{
Id = Int32.Parse(parts[0]),
Engine = parts[1],
Car = Int32.Parse(parts[2])
}).ToList();
That should solve your problem with the error

If your header row is in CSV file then
Car = Int32.Parse(parts[2])
is trying to parse "Car" to Int32.
Try reading file with:
File.ReadAllLines("testing.csv").Skip(1)
to skip header row.

Related

Is there a way to index through objects dnyamically in Scriban with C#?

Working on a PDF generator and it uses the Templating Engine Scriban and LaTeX. Although I cannot seem to reference the C# object Scriban is trying to read via index notation (use the data at this index of the array). I mean something like this:
{~for index in 0..document.template_data.tables.size~}}
{{ document.template_data.tables[index].data_matrix }}
{{ end }}
I get: Object document.template_data.tables[index] is null which ultimately means for whatever reason the compiler cannot retrieve that object.
Q: Is the data actually in the objects?
A: Yes, I hardcoded in numbers like 0 and 1 and got relevant data. This was the case for two of the fields I was trying to access. The issue is trying to dynamically generate the tables.
Q: Does the array have a size?
A: I've looped through in scriban with just the size spitting out. There are 5 tables.
Q: Did you do research?
A: Yes, here are some people on github telling people the issue has been fixed
Does Scriban support .NET Object Indexers?
Accessing object property using indexer notation
Your issue is actually to do with the index out of bounds. You are looping through 0 - the size of the table. In other words, the object is null because you are trying to access past the last entry. I created some super simple POCOs to fill out the objects similar to your code.
public class Document
{
public Data TemplateData { get; set; }
}
public class Data
{
public List<TableRow> Tables { get; set; }
}
public class TableRow
{
public string DataMatrix { get; set; }
}
Here is the updated script to loop through the tables
var bodyTextSub = #"{{~for index in 1..document.template_data.tables.size ~}}
{{index - 1}}: {{ document.template_data.tables[index-1].data_matrix }}
{{ end }}";
var doc = new Document
{
TemplateData = new Data
{
Tables = new List<TableRow>
{
new TableRow {DataMatrix = "This works!!"},
new TableRow {DataMatrix = "Row 2"},
new TableRow {DataMatrix = "Row 3"},
new TableRow {DataMatrix = "Row 4"},
new TableRow {DataMatrix = "Row 5"}
}
}
};
var template2 = Template.Parse(bodyTextSub);
var result2 = template2.Render(new {document = doc});
Console.WriteLine(result2);
Here is the result:
0: This works!!
1: Row 2
2: Row 3
3: Row 4
4: Row 5

Setting the orderLine property on an item when updating an invoice

My problem is that after creating an invoice, I can never get new line items to reference their corresponding sales order line item.
I have been generating invoices via SuiteTalk. When I initially create an invoice, I empty the lineItemList and add back in the items I need. I make sure the orderLine property matches the sales order line item line number. This works great.
But when I try and update the invoice with additional line items, I can never get the new items to retain their orderLine property. The orderLine property is used for the "Invoiced" column on the Sales Order.
In order to get the referencing to be correct, I need to delete the invoice and create it again with all of the line items I need.
Does anyone know if what I am trying to do is possible?
In this example, I use this CreateInvoice function to create the invoice from scratch and add it to NetSuite. Everything works as expected.
public void CreateInvoice(SalesOrder salesOrder) {
Invoice brandNewInvoice = new Invoice() {
createdFrom = new RecordRef() {
internalId = salesOrder.internalId,
},
entity = salesOrder.entity,
tranDate = endDate,
tranDateSpecified = true,
startDate = startDate,
startDateSpecified = true,
endDate = endDate,
endDateSpecified = true,
itemList = new InvoiceItemList(),
};
invoice.itemList.item = GetInvoiceItemList(salesOrder); //see the function shown further down
netSuiteService.add(brandNewInvoice);
}
In this example, the invoice is already created and so I get it from NetSuite and then replace the existing itemList with a new one. After the update, the invoice will now have NO orderLine property for any of the line items. The invoice also loses its "Created From" field because there are no line items with the orderLine property set.
public void UpdateInvoice(SalesOrder salesOrder, String invoiceInternalId) {
Invoice invoice = GetNetSuiteInvoice(invoiceInternalId);
invoice.itemList.item = GetInvoiceItemList(salesOrder); //see the function shown further down
netSuiteService.update(invoice);
}
this is the function used to create the itemList:
public InvoiceItem[] GetInvoiceItemList(SalesOrder salesOrder) {
InvoiceItem[] invoiceItemList = new InvoiceItem[salesOrder.itemList.item.Length];
for (int i = 0; i < salesOrder.itemList.item.Length; i++) {
SalesOrderItem soItem = salesOrder.itemList.item[i];
double quantity = 1;
invoiceItemList[i] = new InvoiceItem() {
item = new RecordRef() {
internalId = soItem.item.internalId,
name = soItem.item.name,
},
amount = quantity * Double.Parse(soItem.rate),
amountSpecified = true,
quantity = quantity,
quantitySpecified = true,
price = new RecordRef() {
internalId = soItem.price.internalId,
name = soItem.price.name,
},
rate = soItem.rate,
orderLine = soItem.line, //this will establish the link between the invoice and the sales order
orderLineSpecified = true,
taxRate1 = soItem.taxRate1,
taxRate1Specified = true,
};
}
return invoiceItemList;
}
Actually what you are looking for is the intialize operation. You need to use initialize in order for Netsuite to properly fill in the created from and orderline props. From the NS Help there is a C# example of creating a Cash Sale:
private void Initialize()
{
this.login(true);
InitializeRef ref1 = new InitializeRef();
ref1.type = InitializeRefType.salesOrder;
//internal id of the sales order to be converted to cash sale
ref1.internalId = "792";
ref1.typeSpecified = true;
InitializeRecord rec = new InitializeRecord();
rec.type = InitializeType.cashSale;
rec.reference = ref1;
ReadResponse read1 = _service.initialize(rec);
}
This is normal, when transforming a transaction to another transaction (e.g. SO to Inv, PO to IR). When you transform, most of the information from the source transaction will be carried over. Like what you are doing which is creating an Invoice from Sales Order(Base on your code below).
createdFrom = new RecordRef() {
internalId = salesOrder.internalId,
},
You don't need to get the line item information from the Sales Order and put it in the Invoice because it will be pre-populated once you create it form Sales Oder(unless you need to change a value of a line item column).
One behavior of a transformed record(Invoice in your case), if you remove a line item from the Invoice you will lose the link to the Sales order(orderLine) and if you remove all the line item you will totally lose the link between the two transactions (createdfrom). This is what you are experiencing. orderLine/createdFrom is a field populated by the system, it looks like you are populating it but you are not.

Extract data into datatable to draw multiple graphs on one chart

I am working on a project that aims to extract and treat data for statitics purpose.
Let's say I have many elements "E" and each element has a list of fields {F1, F2, F3, ...}.
My main table looks like the following:
I need to extract data by elementID into a data table with "Date" as Key.
[{"key": "date1",
"F1": "value",
"F2": "value"}
,{"key": "date2",
"F1": "value",
"F2": "value"}
,{"key": "date3",
"F1": "value",
"F2": "value"}
,{....}]
My current implementation is the following does the next:
1) Query from database by field and order by date in a Dictionary<DateTime, double>
2) Check and fill missing values in each Dictionary.
3) Loop through the list or Dictionary by key and fill a DataRow a row by row.
I don't think that this is the ultimate solution, I have been trying to optimize code. But I am not really sure in wich layer I should focus. Is there any possible way to get the required structure using a select from database ( no need to further loops ) ?
It's not that clear, however, i assume that you want to group by the date-part of the DateTime and select a dictionary where this date is the key and the value is a List<double>.
Then you don't need a DataTable at all. Instead of a double i would use custom classes with all properties. Assuming that mainTable is already a DataTable with the raw data:
public class ElementMeasurement
{
public Element Element { get; set; }
public double Value { get; set; }
public string Field { get; set; }
public DateTime Date { get; set; }
}
public class Element
{
public int ElementID { get; set; }
public string Name { get; set; }
}
Now you can use Enumerable.GroupBy which is part of System.Linq:
Dictionary<DateTime, List<ElementMeasurement>> dateMeasurements = mainTable.AsEnumerable()
.GroupBy(row => row.Field<DateTime>("Date").Date)
.ToDictionary(g =>
g.Key,
g => g.Select(row => new ElementMeasurement
{
Element = new Element { ElementId = row.Field<int>("ElementId") },
Field = row.Field<string>("Field"),
Value = row.Field<double>("Value"),
TimeOfMeasurement = row.Field<DateTime>("Date")
}).ToList()
);
Edit: "Well I should let you know that it is a huge table with thousands of miles of rows!"
I didnt know that the table was not already in memory. Then this might be too memory expensive. So maybe a loop on the DataReader which yields rows ordered by Date is more efficient. Then you could still use my classes above to keep it readable and maintainable but fill the Dictionary step-by-step.
For example (assuming SQL-Server):
var dateMeasurements = new Dictionary<DateTime, List<ElementMeasurement>>();
using(var con = new SqlConnection("ConnectionString"))
using (var cmd = new SqlCommand("SELECT e.* FROM Elements e ORDER BY Date,ElementId,Field,Value", con))
{
con.Open();
using (var rd = cmd.ExecuteReader())
while(rd.Read())
{
DateTime timeOfMeasurement = rd.GetDateTime(rd.GetOrdinal("Date"));
DateTime dateOfMeasurement = timeOfMeasurement.Date;
List<ElementMeasurement> measurements = null;
if (!dateMeasurements.TryGetValue(dateOfMeasurement, out measurements))
{
measurements = new List<ElementMeasurement>();
dateMeasurements.Add(dateOfMeasurement, measurements);
}
var measurement = new ElementMeasurement
{
Element = new Element { ElementId = rd.GetInt32(rd.GetOrdinal("ElementId")) },
Field = rd.GetString(rd.GetOrdinal("Field")),
Value = rd.GetDouble(rd.GetOrdinal("Value")),
TimeOfMeasurement = timeOfMeasurement
};
measurements.Add(measurement);
}
}

Load data from txt file to comboBox

Hi i have this structure of txt file:
Lukas 1
Zdenek 3
Martin 2
Kate 1
And i need load this data...the name i need load to comboBox...and when i choose from ComboBox for example Lukas, i need to save Name Lukas to variable Name and number 1 to variable Number...
It is possible?
I have this code now...
using (StreamReader reader = new StreamReader(#"C:\Us...nka\example.txt"))
{
string data = "";
data = reader.ReadToEnd().Trim();
}
But i need read separately Name and separately Number...Have you any ideas? Thanks..
You can use File.ReadLines and String.Split:
var lines = File.ReadLines(#"C:\Us...nka\example.txt");
var data = lines.Select(l => l.Split());
I would use a class to store both properties:
public class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
}
Now you can load the persons in a loop or with LINQ:
List<Person> allPersons = data
.Where(arr => arr.Length >= 2 && arr[1].Trim().All(Char.IsDigit))
.Select(arr => new Person
{
PersonName = arr[0].Trim(),
PersonID = int.Parse(arr[1].Trim())
})
.ToList();
Edit:
Yes thanks...but i cant load PersonsName to combobox
You can use a BindingSource for the ComboBox. Then set the DisplayMember and ValueMember properties accordingly:
var bindingSourcePersons = new BindingSource();
bindingSourcePersons.DataSource = allPersons;
personComboBox.DataSource = bindingSourcePersons.DataSource;
personComboBox.ValueMember = "PersonID";
personComboBox.DisplayMember = "PersonName";
First create a class like this:
public class Person {
public string Name {get;set;}
public int Number {get;set;}
}
then you can use Linq to convert the string you read like this:
var people = data
.Split(new {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries)
.Select(d => new Person { Name = d.Split(' ')[0], Value = int.Parse(d.Split(' ')[1])})
.ToList();
Or better you could read your data line by line, like this:
var people = from l in File.ReadLines(#"C:\Us...nka\example.txt")
let parts = l.Split(' ')
select new Person {
Name = parts[0].Trim(),
Value = int.Parse(parts[1].Trim())
};
here is a pseudo:
while the reader is not EndOfStream
read current line
split the line that was just read into a string[] array, the separator being a space
first item in the array would be the name and the second item in the array would be the number.
then you add the item in the combo box. The combobox has an Items collection and an add method, which just takes a System.Object.
http://msdn.microsoft.com/en-us/library/aa983551(v=vs.71).aspx

Linq group by to create nested listview

I need to create a nested listview and found a great article on how to do it, but my situation is a bit different. I am a linq newbie and need a little help please :)
I need to get my data into a format similar in that article to work (on that link above, search for "Configuring the ListView" and see table right above it).
Here is my data:
Format Movie Name Price
DVD Star Wars 12.99
DVD Star Wars II 13.99
Blue-Ray Star Wars 15.99
Blue-Ray Star Wars II 17.99
Here is what I have, which isn't really that close, but it is as far as I could get:
var MoviesToBuy = from Movie in dtMovieListDetails.AsEnumerable()
//join MovieDetails in dtMovieListDetails.AsEnumerable() on (string)Movie["ID"] equals (string)MovieDetails["ID"]
group Movie by new
{
Format = Movie["Format"],
Movies = Movie
} into grp
select new
{
Format = (string)grp.Key.Format,
Movies = grp.Key.Movies
};
MoviesToBuy = MoviesToBuy.OrderBy(p => p.Format);
lvwAmazonMovieGroup.DataSource = MoviesToBuy.ToList();
lvwAmazonMovieGroup.DataBind();
I have 3 specific issues/questions:
1.) What I have doesn't work. Since my second column in the group equates to all rows, no actual group is created.
2.) Despite prior issue, I am also getting "Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource" error. In this case, the Movies column is being created as a DataRow datatype. Not sure if that is what is creating the problem. Can I cast on that field somehow?
3.) how can I sort on the fields in the movies. I.e. in the end I want the data to be sorted by Format then Movie Name so the nested list view looks like this:
Blue-Ray
Star Wars 12.99
Star Wars II 13.99
DVD
Star Wars 15.99
Star Wars II 17.99
Any points are greatly appreciated!
Thanks in advance,
Chad
I was thinking you could start with the following, adjusting for the proper variable names and AsEnumerable(), etc.
It orders your movies as you want and puts them in a nested structure as you requested:
var moviesToBuy = from movie in dtMovieListDetails
orderby movie.Format, movie.Price
group movie by movie.Format into grp
select new
{
Format = grp.Key,
Movies = grp.Select (g => new { MovieName = g.MovieName, Price = g.Price })
};
Here's an example program that implements the above query.
Try something like this:
var res = from m in movies
group m by m.Format into grouped
orderby grouped.Key
select new
{
Format = grouped.Key,
Movies = grouped.AsEnumerable().OrderBy(x => x.MovieName)
};
Alternatively
var res = from m in movies
orderby m.MovieName
group m by m.Format into grouped
orderby grouped.Key
select new
{
Format = grouped.Key,
Movies = grouped.AsEnumerable()
};
Using this seed data:
var movies = new[] {
new Movie { Format = "DVD", MovieName = "SW1"},
new Movie { Format = "Blue-ray", MovieName = "SW1"},
new Movie { Format = "DVD", MovieName = "SW2"},
new Movie { Format = "Blue-ray", MovieName = "SW2"},
new Movie { Format = "DVD", MovieName = "RF"}
};
Produced:
Format: Blue-ray
Movie: SW1
Movie: SW2
Format: DVD
Movie: RF
Movie: SW1
Movie: SW2
Just for completness, I used this code to generate the previous list
foreach (var item in res)
{
Console.WriteLine("Format: " + item.Format);
foreach (var item2 in item.Movies)
{
Console.WriteLine("\tMovie: " + item2.MovieName);
}
}

Categories

Resources