I have DataTable object with test data:
DataTable testData = new DataTable();
I'd like to assign data to variables which are the same as column names. I can do it like this:
string foo = testData.Rows[1]["foo"].ToString();
string bar = testData.Rows[1]["bar"].ToString();
or:
string foo = testData.Rows[1][nameof(foo)].ToString();
string bar = testData.Rows[1][nameof(bar)].ToString();
But, I dont want to call variable name every time, I'd like to use somethink like this:
string foo = testData.Rows[1][nameof(this)].ToString();
string bar = testData.Rows[1][nameof(this)].ToString();
Is it possible?
Why do you considder
string foo = testData.Rows[1][nameof(foo)].ToString();
to me more elegant than
string anyName = testData.Rows[1]["foo"].ToString();
You´d have to provide the name anyway. However variable-names don´t mean anything and are just arbitrary to be more readable.
Instead of relying on variable-names why not create a list of names and access the rows by the elements wthin that list?
var myList = new List<string> {"foo", "bar", ... };
Now you can just loop your list and get the rows value:
foreach(var name in myList)
{
var a = testData.Rows[1][name].ToString();
// do something with a
}
You probably want a class to represent the data in the row. You could then populate an object with those properties, either using reflection or serialization. Here's a possibility using simple reflection:
class MyRow
{
public string foo { get; set; }
public string bar { get; set; }
}
var row = testData.Rows[1];
var myRow = new MyRow();
foreach (DataColumn col in testData.Columns)
{
var prop = typeof(MyRow).GetProperty(col.ColumnName);
prop.SetValue(myRow, (string)(row[col] ?? string.Empty), null);
}
You now have an object that has properties of foo and bar.
Or, using serialization instead, it looks like the DataTable serializes nicely into a collection of objects so you can serialize the whole table, then grab the record you want after you deserialize the table:
var tableJson = JsonConvert.SerializeObject(testData);
myRow = JsonConvert.DeserializeObject<MyRow[]>(tableJson)[1];
loop through columns:
foreach(DataColumn col in testData.Columns)
{
var colValue = testData.Rows[1][col.ColumnName];
}
Related
I have the following problem.
I have these strings with whitespace between them.
"+name:string" "+age:int"
I split them with this code:
List<string> stringValueList = new List<string>();
stringValueList = System.Text.RegularExpressions.Regex.Split(stringValue, #"\s{2,}").ToList<string>();
now the elements of List looks like this
"+name:string"
"+age:int"
Now I want to split these strings and create Objects.
This looks like this:
// Storing the created objects in a List of objects
List<myObject> objectList = new List<myObject>();
for(i = 1; i < stringValueList.Count ; i+=2)
{
myObject object = new myObject();
object.modifier = '+';
object.name = stringValueList[i-1].Trim('+'); // out of the example the object.name should be "name"
object.type = stringValueList[i]; // out of the example the object.type value should "string"
objectList.Add(object);
}
At the end I should get two objects with these values:
List<myObject> objectList{ myObject object1{modifier = '+' , name ="name" , type="string"}, myObject object2{modifier='+', name="age" type="int"}}
But my result looks like this:
List<myObject> objectList {myObject object1 {modifier='+', name="name:string" type="+age:int"}}
So instead of getting 2 Objects, I am getting 1 Object. It puts both strings into the elements of the first object.
Can anyone help me out? I guess my problem is in the for loop because i-1 value is the first string in the List and i is the second string but I cant change this.
I guess my problem is in the for loop because i-1 value is the first string in the List and i is the second string but I cant change this.
I don't know why you do i += 2, because apparently you want to split each string in two again. So just have to change that.
Use foreach(), and inside your loop, split your string again:
foreach (var stringValue in stringValueList)
{
myObject object = new myObject();
var kvp = stringValue.Split(':');
object.modifier = '+';
object.name = kvp[0].Trim('+');
object.type = kvp[1];
objectList.Add(object);
}
Of course this code assumes your inputs are always valid; you'd have to add some boundary checks to make it more robust.
Alternatively, you could expand your Regex formula to do the whole thing in one go.
For example, with (?<=")[+](.*?):(.*?)(?="), all you'd have to do is assign the matched group values.
foreach (Match m in Regex.Matches(stringValue, "(?<=\")[+](.*?):(.*?)(?=\")"))
{
myObject obj = new myObject
{
modifier = '+',
name = m.Groups[1].Value,
type = m.Groups[2].Value
};
objectList.Add(obj);
}
It's interesting to see how others approach a problem. I would have done something like this:
public class MyObject
{
public char Modifier { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public static IEnumerable<MyObject> Parse(string str)
{
return str
.Split(' ')
.Where(s => string.IsNullOrEmpty(s) == false)
.ToList()
.ForEach(i =>
{
var sections = i.Remove(0, 1).Split(':');
return new MyObject()
{
Modifier = i[0],
Name = sections[0],
Type = sections[1]
};
});
}
}
I'm using FastMember.ObjectReader to copy a list of structs to a DataTable, which I then use as the DataSource of a gridview:
struct Foo {
[DisplayName("title1")]
public string Bar { get; set; }
}
...
var rows = new List<Foo>();
rows.Add(new Foo { Bar = "somethingsomething" });
DataTable table = new DataTable();
using (var reader = ObjectReader.Create(rows)) {
table.Load(reader);
}
grid.DataSource = table.DefaultView;
If I select the list itself as the DataSource, the DisplayNames are used as column titles instead of the struct member name:
How can I recreate that when using FastMember.ObjectReader?
Oh, I see what you mean; you want the IDataReader to expose the [DisplayName] in the metadata; however, the primary way that is exposed is via GetSchemaTable(), and AFAIK there is no recognised key to represent [DisplayName]. It would be incorrect to pass that as the name, IMO.
Running a quick test:
var table = new DataTable();
table.Columns.Add("foo").Caption = "bar";
var schema = table.CreateDataReader().GetSchemaTable();
foreach(DataRow row in schema.Rows)
{
foreach(DataColumn col in schema.Columns)
{
Console.WriteLine($"{col.ColumnName}={row[col]}");
}
Console.WriteLine();
}
shows that indeed it is unlikely to expect it there:
ColumnName=foo
ColumnOrdinal=0
ColumnSize=-1
NumericPrecision=
NumericScale=
DataType=System.String
ProviderType=
IsLong=False
AllowDBNull=True
IsReadOnly=False
IsRowVersion=False
IsUnique=False
IsKey=False
IsAutoIncrement=False
BaseCatalogName=
BaseSchemaName=
BaseTableName=
BaseColumnName=foo
AutoIncrementSeed=0
AutoIncrementStep=1
DefaultValue=
Expression=
ColumnMapping=1
BaseTableNamespace=
BaseColumnNamespace=
this means that there isn't really anything I can suggest other than to manually populate the .Caption, perhaps use fast-member to get the data.
I have code like below (I've generified and reduced it to represent just the issue at hand). The code works, that is it takes in a DataGridView.DataSource and ulitmately, using EPPlus, outputs the data to an Excel file. My question relates to covariance and how to use it, I think.
So you see it builds newList based on the type that it has found in the DataSource. Then a little further down it adds the data using the Properties, someClassObject.Name, .Address and .Phone that are unique to this type.
My problem is that there are about 75 different classes that could be passed in through the DataGridView parameter. Each class has its own unique properties (i.e. not necessarily Name, Address, Phone) though all of the objects in given DataGridView.DataSource are of the same class.
I could have a giant switch statement based on type.FullName and then each would have its own for loop to assign the Property values to the cell. That would work but would be incredibly cumbersome. Is there a better way to do this?
public void openExcelReport(ref DataGridView dataGridView, bool bolSave = false, bool bolOpen = true, string pageTitle = "EXPORTED DATA")
{
// put dataGridView.DataSource into a List
object firstItem = null;
var myDataSource = dataGridView.DataSource;
var myList = ((System.Windows.Forms.BindingSource)dataGridView.DataSource).List;
firstItem = ((System.Collections.IList)myList)[0];
var type = firstItem.GetType();
Type PROJECT1_TYPE = typeof(Project1.SomeClass);
Type PROJECT2_TYPE = typeof(Project2.SomeOtherClass); // many more of these
dynamic newList = null;
if (type.FullName.Equals(PROJECT1_TYPE.FullName))
{
newList = new List<Project1.SomeClass>();
foreach (Project1.SomeClass someClassObject in myList)
{
newList.Add(someClassObject);
}
}
ExcelPackage package = new ExcelPackage();
using ((package)) // use EPPlus
{
// Create the worksheet
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Worksheet 1");
// Load the datatable into the sheet, starting from cell A1. Print the column names on row 1
System.Data.DataTable dataTable = new System.Data.DataTable();
dataTable.Columns.Add("Id");
dataTable.Columns.Add("FirstColumn", typeof(string));
dataTable.Columns.Add("SecondColumn", typeof(string));
dataTable.Columns.Add("ThirdColumn", typeof(string));
dataTable.Columns[0].AutoIncrement = true;
var column_id = 0;
foreach (Project1.SomeClass someClassObject in "FirstColumn")
{
DataRow dataRow = dataTable.NewRow();
dataRow["FirstColumn"] = someClassObject.Name;
dataRow["SecondColumn"] = someClassObject.Address;
dataRow["ThirdColumn"] = someClassObject.Phone
dataTable.Rows.Add(dataRow);
column_id += 1;
}
// worksheet is now populated, so save Excel File
...
}
Instead of doing the DataRow creation within this function, you could move it out to the class implementations using a common interface to enforce it, for instance:
public interface DataRowConvertable
{
DataRow GetDataRow();
}
public class SomeClass : DataRowConvertable
{
public SomeClass() { }
public SomeClass(string name, string address, string phone)
{
Name = name;
Address = address;
Phone = phone;
}
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public DataRow GetDataRow()
{
DataRow row = GetDataTable().NewRow();
row["Name"] = this.Name;
row["Address"] = this.Address;
row["Phone"] = this.Phone;
return row;
}
public static DataTable GetDataTable()
{
DataTable table = new DataTable("SomeClassTable");
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Address", typeof(string));
table.Columns.Add("Phone", typeof(string));
return table;
}
}
You could take it further, but this should give you a good alternative and a place to start. You can either leave the GetDataTable function public, and use that as well to create your table instance, or make it private and only use it internally. I would opt for the former and use that in your function to initialize the table before filling it. You could even get rid of the static modifier and add it to your interface, but I prefer the static usage of it in this instance since it is not reliant on the instance of the class and the data involved, only on the structure.
Either way, you could then change the code you have above to look like this:
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Worksheet 1");
System.Data.DataTable dataTable = Project1.SomeClass.GetDataTable();
foreach (Project1.SomeClass someClassObject in myList)
{
dataTable.Rows.Add(someClassObject.GetDataRow());
}
If you need an incremented id column, you could easily add that in the GetDataTable/GetDataRow functions and update them just as you were above.
This is just a quick example, it could very likely be cleaned up and optimized some, but it still conveys the idea. Hope it helps you out some.
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);
}
}
I have the following class in my C# .NET 3.5 win forms app:
class Field {
string objectName;
string objectType;
string fieldName;
string fieldValue;
}
and a List fieldList that is a datasource for a checkedlistbox. This listbox shows all the distinct objectNames from my fieldList collection.
I want to create another checkedlistbox that contains fieldNames, but only shows fieldnames that have an associated checked objectName in the first list box.
So my question is how can I query the DataSource of the original list of objectNames to return the distinct set of fieldNames that are associated with a selected objectName?
That is not very easy to read so I will give an example:
Field1 {
objectName = 'objA'
FieldName = 'FieldA'
}
Field2 {
objectName = 'objA'
FieldName = 'FieldB'
}
Field3 {
objectName = 'objB'
FieldName = 'FieldA'
}
Field4 {
objectName = 'objC'
FieldName = 'FieldC'
}
So suppose in my checkbox I select objectNames objA and objB. Then my returned fields would be 'FieldA' and 'FieldB'.
How can I achieve this using LINQ or filtering my generic list of Fields? Can I utilise the 'select' or 'where' methods that are available to a list?
First, read the object names into an array or list; I'll fake that part. Then it should be something like:
string[] objectNames = { "objA", "objC" };
var hashSet = new HashSet<string>(objectNames);
var qry = (from row in data
where hashSet.Contains(row.objectName)
select row.fieldName).Distinct().ToList();
(edit)
To get the selected names (the bit I faked) you could try (untested):
var selectedNames = namesCheckedListBox.CheckedItems.Cast<Field>()
.Select(field => field.objectName);
var hashSet = new HashSet<string>(selectedNames);
(note no need to use Distinct() in the above, since HashSet<T> does that anyway)
var selectedNames = ... // List of selected names
var selectedFields = (from f in fieldList
where selectedNames.Contains(f.objectName)
select f.FieldName).Distinct().ToList();