I have a DataGridView used for displaying the data for a custom class, foo.
public class foo
{
public string header{get;set;}
public int value{get;set;}
public bool isHidden{get;set;}
}
Foo gets read from the database and gets processed from a JSON string and then inserted into the DataGridView for display to the user.
List<foo> listOfFoo = new List<foo>();
DataTable fooTable = new DataTable();
myDataGridView.DataSource = fooTable;
private void getFoo()
{
JavaScriptSerializer js = new JavaScriptSerializer();
string getData = myWebClient.DownloadString(/*url of server*/);
Dictionary<string,object> fooArg = (Dictionary<string,object>)js.DeserializeObject(getData);
object[] foo = (object[])fooArg["foo"];
for(int i = 0; i < foo.Length; i++)
{
Dictionary<string,object> fooStep = (Dictionary<string,object>)foo[i];
foo temp = new foo();
temp.header = (string)fooStep["header"];
temp.value = (int)fooStep["value"];
temp.isHidden = (bool)fooStep["isHidden"];
listOfFoo.Add(temp);
}
drawFoo();
}
private void drawFoo()
{
fooTable.Rows.Clear();
for(int i = 0; i < listOfFoo.Count; i++)
{
//myDataGridView has two columns, header and value. isHidden is not shown in the table itself
foo i = listOfFoo.ElementAt(i);
fooTable.Rows.Add(i.header, i.value);
}
}
The problem I'm having is that I'm using the index of the row to preserve the order of the foos (DataGridView is not sortable). If I modified my drawFoo function to the below, will the indexing be preserved? I have an insert function that inserts a new foo into the list below the one currently selected in the DataGridView. Would it mess up the indexing, considering when I post the listOfFoo back to the server it has to be in the appropriate order. Is there a better way to handle this? I'm open for suggestions. Thanks, people smarter than I am!
private void drawFoo()
{
fooTable.Rows.Clear();
for(int q = 0; q < listOfFoo.Count; q++)
{
//myDataGridView has two columns, header and value. isHidden is not shown in the table itself
foo i = listOfFoo.ElementAt(q);
fooTable.Rows.Add(i.header, i.value);
if(i.isHidden)
myDataGridView.Rows[q].Visible = false;
}
}
Related
I have below class. And data is serialized using JsonConvert.DeserializeObject. The data is returning from list. But I want to loop using for or for each. Please advice.
Thank you.
var result = client.getInvocieLine
(call Context);
string strResultJson = JsonConvert.SerializeObject(result);
System.IO.File.WriteAllText(#"D:\jsondata\invoicedata.json", strResultJson);
string fileName = #"D:\jsondata\invoicedata.json";
string jsonText = System.IO.File.ReadAllText(fileName)
List<EInvoiceModel.Class1> data = Newtonsoft.Json.JsonConvert.DeserializeObject<List<EInvoiceModel.Class1>>(jsonText);
EInvoiceModel.Invoiceline invoicelined = new EInvoiceModel.Invoiceline();
for(int i=0; i < data.Count; i++)
{
invoicelined.parmItemId = data[i].invoiceLines[i].parmItemId; //Its bring only one record. I need all record from data. Please advice.
}
return Ok(invoicelined);
Please find my EInvoiceModel contains below structure, Which i designed as per my json response. yes I need same index for data and invoiceLines in data[i].invoiceLines[i]. How can I get this to return all values. Index currently pointing only one record and return one record. If I change position its return another record. But I need all please.
public class Class1
{
public Invoiceline[] invoiceLines { get; set; }
public string parmCustName { get; set; }
public DateTime parmInvoiceDate { get; set; }
} public class Invoiceline
{
public string parmItemId { get; set; }
public string parmItemNameDisplay { get; set; }
public float parmQty { get; set; }}
Please find my json structure. I wist to repeat all parameter-ids from json response.
{"invoiceLines":[{"parmCurrencyCode":null,"parmCustExchRate":0.0,"parmInvoiceId":null,"parmItemId":null,"parmItemNameDisplay":null,"parmQty":0.0,"parmSalesLinePercent":0.0,"parmSalesUnit":null,"parmdiscountAmount":0.0,"parmnetTotal":0.0,"parmsalesPrice":0.0,"parmsalesTotal":0.0,"parmtotalItemsLineDisc":0.0,"parmtotalTaxableFees":0.0}],"parmCustName":null,"parmInvoiceDate":"0001-01-01T00:00:00","parmInvoiceId":null,"parmSalesId":null,"parmnetAmount":0.0,"parmtotalAmount":0.0,"parmtotalAmountWithDisc":0.0,"parmtotalItemsDiscountAmount":0.0,"parmtotalSalesAmount":0.0}
Please I want return all parmItemId below way. I don't want damage my json structure.
EInvoiceModel.Class1 ds = new EInvoiceModel.Class1();
List<EInvoiceModel.Class1> data = Newtonsoft.Json.JsonConvert.DeserializeObject<List<EInvoiceModel.Class1>>(jsonText);
// List<EInvoiceModel.Invoiceline> data = Newtonsoft.Json.JsonConvert.DeserializeObject<List<EInvoiceModel.Invoiceline>>(jsonText);
for (int i = 0; i < data.Count; i++)
{
ds.invoiceLines = new List<EInvoiceModel.Invoiceline>
{
new EInvoiceModel.Invoiceline
{
parmItemId = data[i].invoiceLines[i].parmItemId
}
};
}
return Ok(ds);
You have used to Generic list
To get all list
List<EInvoiceModel.Invoiceline> data = Newtonsoft.Json.JsonConvert.DeserializeObject<List<EInvoiceModel.Invoiceline>>(jsonText);
List<EInvoiceModel.Invoiceline> invoicelines = new List<EInvoiceModel.Invoiceline>();
for(int i=0; i < data.Count; i++)
{
invoicelines.Add(new EInvoiceModel.Invoiceline()
{
parmItemId = data[i].parmItemId
});
}
return Ok(invoicelines);
You now have this:
EInvoiceModel.Class1 ds = new EInvoiceModel.Class1();
for (int i = 0; i < data.Count; i++)
{
ds.invoiceLines = new List<EInvoiceModel.Invoiceline>
{
new EInvoiceModel.Invoiceline
{
parmItemId = data[i].invoiceLines[i].parmItemId
}
};
}
This means that on every iteration you overwrite that ds.invoiceLines with a new list, containing a single item. So you end up with only the last one.
A possibly better way would be this:
EInvoiceModel.Class1 ds = new EInvoiceModel.Class1();
ds.invoiceLines = new List<EInvoiceModel.Invoiceline>(); // create a list once
for (int i = 0; i < data.Count; i++)
{
// add one item to that list
ds.invoiceLines.Add(new EInvoiceModel.Invoiceline
{
parmItemId = data[i].invoiceLines[i].parmItemId
}
);
}
This way you assign a new (empty) list once (you can skip this when invoiceLines already contains an empty list, after you created a new Class1).
And then, in the loop, you don't assign a new list, but just add a new item to that existing list. At the end, you get a list with as many items as there are in your input data.
By the way, this assumes that you really need to make a list of data[i].invoiceLines[i].parmItemId. I find it curious that for data[3] you need an invoiceLines[3] and for data[7] an invoiceLines[7]. I would expect one loop through all data and then an inner loop over all its invoiceLines. But you know your data best.
Apparently, you do need to loop over the inner objects. So modify that code like this:
EInvoiceModel.Class1 ds = new EInvoiceModel.Class1();
ds.invoiceLines = new List<EInvoiceModel.Invoiceline>(); // create a list once
// loop over all "data" items
for (int i = 0; i < data.Count; i++)
{
// then loop over the invoiceLines inside the current data item
for (int j = 0; j < data[i].invoiceLines.Count(); j++)
{
// add one item to that list
ds.invoiceLines.Add(new EInvoiceModel.Invoiceline
{
parmItemId = data[i].invoiceLines[j].parmItemId
}
);
}
}
Note that I still use index i in data[i] from the outer loop, but now use index j in invoiceLines[j] from the inner loop.
I'm trying to test a form by submitting a combination of all values to see if it breaks. These are ComboBoxes that I have stored in an ExtraField class
public class ExtraField
{
public String Name = ""; //name of form key
public Dictionary<String, String> Options = new Dictionary<String, String>(); //Format: OptionText, Value
}
I have generated a list of these fields
List<ExtraField> efList = new List<ExtraField>();
I was thinking all possible combinations of these fields could be added to a string list that I can parse (I was thinking name=opt|name=opt|name=opt). I've provided an example of what would work below (where ExtraField list Count==3):
List<ExtraField> efList = new List<ExtraField>();
ExtraField f1 = new ExtraField();
f1.Name = "name1";
f1.Options.Add("text", "option1");
f1.Options.Add("text2", "option2");
f1.Options.Add("text3", "option3");
efList.Add(f1);
ExtraField f2 = new ExtraField();
f2.Name = "name2";
f2.Options.Add("text", "option1");
f2.Options.Add("text2", "option2");
f2.Options.Add("text3", "option3");
f2.Options.Add("text4", "option4");
efList.Add(f2);
ExtraField f3 = new ExtraField();
f3.Name = "name3";
f3.Options.Add("text2", "option1");
f3.Options.Add("text3", "option2");
f3.Options.Add("text4", "option3");
f3.Options.Add("text5", "option4");
f3.Options.Add("text6", "option5");
efList.Add(f3);
Should produce
name1=option1|name2=option1|name3=option1
name1=option1|name2=option1|name3=option2
name1=option1|name2=option1|name3=option3
name1=option1|name2=option1|name3=option4
name1=option1|name2=option1|name3=option5
name1=option1|name2=option2|name3=option1
name1=option1|name2=option2|name3=option2
name1=option1|name2=option2|name3=option3
name1=option1|name2=option2|name3=option4
name1=option1|name2=option2|name3=option5
name1=option1|name2=option3|name3=option1
name1=option1|name2=option3|name3=option2
name1=option1|name2=option3|name3=option3
name1=option1|name2=option3|name3=option4
name1=option1|name2=option3|name3=option5
name1=option1|name2=option4|name3=option1
name1=option1|name2=option4|name3=option2
name1=option1|name2=option4|name3=option3
name1=option1|name2=option4|name3=option4
name1=option1|name2=option4|name3=option5
name1=option2|name2=option1|name3=option1
...etc
All ExtraFields in the list need to have a value and I need all permutations in one format or another. It's a big list with a lot of permutations otherwise I'd do it by hand.
Well I did it... But I'm not proud of it. I'm sure there is a better way of doing it recursively. Hopefully this helps someone.
public List<String> GetFormPermutations(List<ExtraField> inList)
{
List<String> retList = new List<String>();
int[] listIndexes = new int[inList.Count];
for (int i = 0; i < listIndexes.Length; i++)
listIndexes[i] = 0;
while (listIndexes[inList.Count-1] < inList.ElementAt(inList.Count-1).Options.Count)
{
String cString = "";
//after this loop is complete. a line is done.
for (int i = 0; i < inList.Count; i++) {
String key = inList.ElementAt(i).Name;
Dictionary<String, String> cOptions = inList.ElementAt(i).Options;
String value = cOptions.ElementAt(listIndexes[i]).Value;
cString += key + "=" + value;
if (i < inList.Count - 1)
cString += "|";
}
retList.Add(cString);
listIndexes[0]++;
for(int i = 0; i < inList.Count -1; i++)
{
if (listIndexes[i] >= inList.ElementAt(i).Options.Count)
{
listIndexes[i] = 0;
listIndexes[i + 1]++;
}
}
}
return retList;
}
UPDATED ANSWER
So I managed to do it recursively. I haven't done this since college :D
Here's the whole class:
https://gist.github.com/Rastamas/8070ae7e1471d2183451a17bcf061376
PREVIOUS ANSWER BELOW
This goes through your list and adds the strings to a StringBuilder in the format you showed
foreach (var item in efList)
{
foreach (var option in item.Options)
{
stringBuilder.Append(String.Format("{0}={1}|", item.Name, option.Value));
}
stringBuilder.Remove(stringBuilder.Length - 1, 1);
stringBuilder.AppendLine();
}
Then you can just use stringBuilder.ToString() to get the whole list.
I have a project that keeping items and itemdata(number) with a sorted why.
I have 2 objects:
1.sorted Combobox that keeps the items(names)
2.sortedlist that keeps the item(name) as key and itemdata(number) as value.
For some reason some character(for example "-") is not sorted the same way in the combo and in the sortedlist.the combo keeping the string with "-" in the first row or the group while in the sortedlist its located in the last row of the group.
this is my code:
public partial class Form1: Form
{
public Form1()
{
InitializeComponent();
}
SortedList<object, object> sortedvbcombo = new SortedList<object, object>();
private void Form1_Load(object sender, EventArgs e)
{
Data[] D = new Data[5];
D[0] = new Data();
D[0].name = "BABA";
D[0].number = 1000;
D[1] = new Data();
D[1].name = "B-REAL";
D[1].number = 1001;
D[2] = new Data();
D[2].name = "BCCDC";
D[2].number = 1002;
D[3] = new Data();
D[3].name = "BAAAAA";
D[3].number = 1003;
D[4] = new Data();
D[4].name = "BFFFFDS";
D[4].number = 1004;
comboBox1.Sorted = true;
for (int i = 0; i < D.Count(); i++)
{
comboBox1.Items.Add(D[i].name);
sortedvbcombo.Add(D[i].name, D[i].number);
}
}
}
struct Data
{
public string name;
public int number;
}
the result for combo box:
B-REAL
BAAAAA
BABA
BCCDC
BFFFFDS
the result for sortedlist:
BAAAAA
BABA
BCCDC
BFFFFDS
B-REAL
why it's not sorting in the same way and if it is possible to align it from the sortedlist properties?
p.s I know there is some possibility to use
sortedlist<object,object> srt = new sortedlist<object,object>(idictionary)
but I couldn't find the way to implement it
one solution i found for now(even though i think the combobox sort is incorrect) is by encoding the data to the sortlist
for (int i = 0; i < D.Count(); i++)
{
comboBox1.Items.Add(D[i].name);
byte[] encbuff = System.Text.Encoding.UTF8.GetBytes(D[i].name);
sortedvbcombo.Add(Convert.ToBase64String(encbuff), D[i].number);
}
is there smarter way to do it with the sortlist or with the combobox sort?
I have a CSV file that I want some values from. One problem is that I don't know how many columns the file has. The number can be different every time I get a new CSV file. It will always have columns and rows with values. I will get it from a normal excel-file.
I want the method to return a List<List>.
ListA(FirstName, LastName, PhoneNumber... and so on) here I don't know how many items ListA will have. It can be different every time.
Inside ListA I want lists of persons like this:
ListA[FirstName] = List1(Zlatan, Lionel, Anders.....)
ListA[LastName] = List2(Ibrahimovic, Messi, Svensson.....) .. and so on.
You could create a class Person
class person {
private string FirstName;
private string LastName;
// others
}
Open the File and split each row in the file with the String.Split()-Method then convert each value and create Objects, which you can add to a List.
List<Person> persons = new List<Person>();
persons.Add(personFromFile);
Thats a pretty short solution but it works
Edit: Variable Fields per Row
If thats the case you could use a List<string[]> stringArraylist; and then add the results of the String.Split()-Method to it.
List<string[]> stringArraylist;
stringArraylist = new List<string[]>();
stringArraylist.Add("Andrew;Pearson;...;lololo;".Split(';'));
Is that more of what you wanted?
There are a lot of questions on SO that deal with parsing CSV files. See here for one: Reading CSV files in C#. I am fairly certain there are some solutions built in to .NET, though I can't recall what they are at the moment. (#ZoharPeled suggested TextFieldParser)
Most of the parsing solutions with give you a collection of rows where each item is a collection of columns. So assuming you have something like a IEnumerable<IList<string>>, you could create a class and use LINQ queries to get what you need:
public class CSVColumns
{
public IEnumerable<IList<string>> CSVContents { get; private set; }
public CSVColumns(IEnumerable<IList<string>> csvcontents)
{
this.CSVContents = csvcontents;
}
public List<string> FirstNames
{
get { return GetColumn("FirstName"); }
}
public List<string> LastNames
{
get { return GetColumn("LastName"); }
}
/// <summary>
/// Gets a collection of the column data based on the name of the column
/// from the header row.
/// </summary>
public List<string> GetColumn(string columnname)
{
//Get the index of the column with the name
var firstrow = CSVContents.ElementAtOrDefault(0);
if (firstrow != null)
{
int index = -1;
foreach (string s in firstrow)
{
index++;
if (s == columnname)
{
return GetColumn(index, true);
}
}
}
return new List<string>();
}
/// <summary>
/// Gets all items from a specific column number but skips the
/// header row if needed.
/// </summary>
public List<string> GetColumn(int index, bool hasHeaderRow = true)
{
IEnumerable<IList<string>> columns = CSVContents;
if (hasHeaderRow)
columns = CSVContents.Skip(1);
return columns.Select(list =>
{
try
{
return list[index];
}
catch (IndexOutOfRangeException ex)
{
return "";
}
}
).ToList();
}
}
I finally got a solution and it's working for me. My friend made it so all creed to him. No user here on stackoverflow so I post it instead.
private List<Attributes> LoadCsv()
{
string filename = #"C:\Desktop\demo.csv";
// Get the file's text.
string whole_file = System.IO.File.ReadAllText(filename);
// Split into lines.
whole_file = whole_file.Replace('\n', '\r');
string[] lines = whole_file.Split(new char[] { '\r' },
StringSplitOptions.RemoveEmptyEntries);
// See how many rows and columns there are.
int num_rows = lines.Length;
int num_cols = lines[0].Split(';').Length;
// Allocate the data array.
string[,] values = new string[num_rows, num_cols];
// Load the array.
for (int r = 0; r < num_rows; r++)
{
string[] line_r = lines[r].Split(';');
for (int c = 0; c < num_cols; c++)
{
values[r, c] = line_r[c];
}
}
var attr = new List<Attributes>();
for (var r = 0; r < num_rows; r++)
{
if (r == 0)
{
for (var c = 0; c < num_cols; c++)
{
attr.Add(new Attributes());
attr[c].Name = values[r, c];
attr[c].Value = new List<String>();
}
}
else
{
for (var b = 0; b < num_cols; b++)
{
var input = values[r, b];
attr[b].Value.Add(input);
}
}
}
// Return the values.
return attr;
}
I have a CSV that is delivered to my application from various sources. The CSV will always have the same number columns and the header values for the columns will always be the same.
However, the columns may not always be in the same order.
Day 1 CSV may look like this
ID,FirstName,LastName,Email
1,Johh,Lennon,jlennon#applerecords.com
2,Paul,McCartney,macca#applerecords.com
Day 2 CSV may look like this
Email,FirstName,ID,LastName
resident1#friarpark.com,George,3,Harrison
ringo#allstarrband.com,Ringo,4,Starr
I want to read in the header row for each file and have a simple mechanism for associating each "column" of data with the associated property I have defined in my class.
I know I can use selection statements to figure it out, but that seems like a "bad" way to handle it.
Is there a simple way to map "columns" to properties using a dictionary or class at runtime?
Use a Dictionary to map column heading text to column position.
Hard-code mapping of column heading text to object property.
Example:
// Parse first line of text to add column heading strings and positions to your dictionary
...
// Parse data row into an array, indexed by column position
...
// Assign data to object properties
x.ID = row[myDictionary["ID"]];
x.FirstName = row[myDictionary["FirstName"]];
...
You dont need a design pattern for this purpose.
http://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader
I have used this Reader, while it is pretty good, it has a functionality as row["firstname"] or row["id"] which you can parse and create your objects.
I have parsed both CSV files using Microsoft.VisualBasic.FileIO.TextFieldParser. I have populated DataTable after parsing both csv files:
DataTable dt;
private void button1_Click(object sender, EventArgs e)
{
dt = new DataTable();
ParseCSVFile("day1.csv");
ParseCSVFile("day2.csv");
dataGridView1.DataSource = dt;
}
private void ParseCSVFile(string sFileName)
{
var dIndex = new Dictionary<string, int>();
using (TextFieldParser csvReader = new TextFieldParser(sFileName))
{
csvReader.Delimiters = new string[] { "," };
var colFields = csvReader.ReadFields();
for (int i = 0; i < colFields.Length; i++)
{
string sColField = colFields[i];
if (sColField != string.Empty)
{
dIndex.Add(sColField, i);
if (!dt.Columns.Contains(sColField))
dt.Columns.Add(sColField);
}
}
while (!csvReader.EndOfData)
{
string[] fieldData = csvReader.ReadFields();
if (fieldData.Length > 0)
{
DataRow dr = dt.NewRow();
foreach (var kvp in dIndex)
{
int iVal = kvp.Value;
if (iVal < fieldData.Length)
dr[kvp.Key] = fieldData[iVal];
}
dt.Rows.Add(dr);
}
}
}
}
day1.csv and day2.csv as mentioned in the question.
Here is how output dataGridView1 look like:
Here is a simple generic method that will take a CSV file (broken into string[]) and create from it a list of objects. The assumption is that the object properties will have the same name as the headers. If this is not the case you might look into the DataMemberAttribute property and modify accordingly.
private static List<T> ProcessCSVFile<T>(string[] lines)
{
List<T> list = new List<T>();
Type type = typeof(T);
string[] headerArray = lines[0].Split(new char[] { ',' });
PropertyInfo[] properties = new PropertyInfo[headerArray.Length];
for (int prop = 0; prop < properties.Length; prop++)
{
properties[prop] = type.GetProperty(headerArray[prop]);
}
for (int count = 1; count < lines.Length; count++)
{
string[] valueArray = lines[count].Split(new char[] { ',' });
T t = Activator.CreateInstance<T>();
list.Add(t);
for (int value = 0; value < valueArray.Length; value++)
{
properties[value].SetValue(t, valueArray[value], null);
}
}
return list;
}
Now, in order to use it just pass your file formatted as an array of strings. Let's say the class you want to read into looks like this:
class Music
{
public string ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
So you can call this:
List<Music> newlist = ProcessCSVFile<Music>(list.ToArray());
...and everything gets done with one call.