I'd like to use LINQ to take a datarow, and parse out the datacolumn names with their values.
So if I had a dataRow with the following columns and values:
DataColumn column1 with value '1'
DataColumn column2 with value 'ABC'
I'd like to have a string returned as "column1 = '1' and column2 = 'ABC'"
**** code should not care about the column names, nor the number of columns in the table.****
Purpose being, to filter a dataTable like:
var newRows = myTable.Select ("column1 = '1' and column2 = 'ABC'");
I can parse out the columns of the table like this:
string[] columnName = myTable.Columns.Cast<DataColumn>().Select(cn => cn.ColumnName).ToArray();
But I need to also extract values from a target row.
It feels like this might be a start:
{
string[] columnNames = existingTable.Columns.Cast<DataColumn>().Select(cn => cn.ColumnName).ToArray();
foreach (DataRow oldRow in existingTable.Rows)
{
var criteria = string.Join("and", columnNames, oldRow.ItemArray);
}
}
I think you are trying to get the column names and rows without actually referencing them. Is this what you're trying to do:
var table = new DataTable();
var column = new DataColumn {ColumnName = "column1"};
table.Columns.Add(column);
column = new DataColumn {ColumnName = "column2"};
table.Columns.Add(column);
var row = table.NewRow();
row["column1"] = "1";
row["column2"] = "ABC";
table.Rows.Add(row);
string output = "";
foreach (DataRow r in table.Rows)
{
output = table.Columns.Cast<DataColumn>()
.Aggregate(output, (current, c) => current +
string.Format("{0}='{1}' ", c.ColumnName, (string) r[c]));
output = output + Environment.NewLine;
}
// output should now contain "column1='1' column2='ABC'"
You can create extension methods out of this too, which operate both on a DataTable (for all rows) or a DataRow (for a single row):
public static class Extensions
{
public static string ToText(this DataRow dr)
{
string output = "";
output = dr.Table.Columns.Cast<DataColumn>()
.Aggregate(output, (current, c) => current +
string.Format("{0}='{1}'", c.ColumnName, (string)dr[c]));
return output;
}
public static string ToText(this DataTable table)
{
return table.Rows.Cast<DataRow>()
.Aggregate("", (current, dr) => current + dr.ToText() + Environment.NewLine);
}
}
I would highly recommend mapping to a class, and then adding another property which combines both of them!
See if this gets you in the right direction
EDIT Added solution using reflection since that is more in line with what you are wanting to accomplish
void Main()
{
List<MyClass> myColl = new List<MyClass>() { new MyClass() { myFirstProp = "1", mySecondProp = "ABC" } };
foreach (MyClass r in myColl)
{
List<string> rPropsAsStrings = new List<string>();
foreach (PropertyInfo propertyInfo in r.GetType().GetProperties())
{
rPropsAsStrings.Add(String.Format("{0} = {1}", propertyInfo.Name, propertyInfo.GetValue(r, null)));
}
Console.WriteLine(String.Join(" and ", rPropsAsStrings.ToArray()));
}
}
public class MyClass
{
public string myFirstProp { get; set; }
public string mySecondProp { get; set; }
}
The below uses Linq with strong typed properties
System.Data.DataTable table = new DataTable("ParentTable");
DataColumn column;
DataRow row;
column = new DataColumn();
column.DataType = typeof(string);
column.ColumnName = "column1";
table.Columns.Add(column);
column = new DataColumn();
column.DataType = typeof(string);
column.ColumnName = "column2";
table.Columns.Add(column);
row = table.NewRow();
row["column1"] = "1";
row["column2"] = "ABC";
table.Rows.Add(row);
var results = from myRow in table.AsEnumerable()
select String.Format("column1 = {0}, column2 = {1}", myRow[0], myRow[1]);
foreach (string r in results)
{
Console.WriteLine(r);
}
Related
I currently have this code, I'm aware how to print out the rows, but I can't figure out how to get my column headers? I don't want to use the solution I had that I commented out because I want to make the code generic so that I can use it for other lists too.
static DataTable ConvertListToDataTable(List<List<string>> list)
{
// New table.
DataTable table = new DataTable();
/* table.Columns.Add("Employee ID");
table.Columns.Add("First Name");
table.Columns.Add("Last Name");
table.Columns.Add("Job Title");
table.Columns.Add("Address");
table.Columns.Add("City");
*/
foreach(List<string> row in list) {
table.Rows.Add(row.ToArray());
}
return table;
}
It's impossible to derive the column headers from the List<List<string>> since the information is simply not available. You could provide them per parameter:
static DataTable ConvertListToDataTable(List<List<string>> list, IList<string> columnNames)
{
DataTable table = new DataTable();
foreach (string columnName in columnNames)
table.Columns.Add(columnName);
foreach (List<string> row in list)
{
if (row.Count != columnNames.Count)
throw new ArgumentException(string.Format("Invalid data in list, must have the same columns as the columnNames-argument. Line was: '{0}'", string.Join(",", row)), "list");
DataRow r = table.Rows.Add();
for (int i = 0; i < columnNames.Count; i++)
r[i] = row[i];
}
return table;
}
How to use:
string[] columns = { "Employee ID", "First Name", "Last Name", "Job Title", "Address", "City"};
DataTable tblEmployee = ConvertListToDataTable(employees, columns);
But instead of using a List<List<string>>(or a DataTable) to store your employees you should use a custom class, for example Employee with all those properties. Then you can fill a List<Employee>. On that way your code is much better to read and to maintain.
The following code gives you the facility to convert an IEnumerable type to DataTable with dynamic Headers using System.Reflection.PropertyInfo. try to use this.
public static DataTable EnumerableToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null) return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create table, Only first time, others will follow
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}
I need to convert a list array of type List<string>[] to Datatable in C#.
I have found many topics related to List<string[]> to Datatable conversion but nothing on the conversion I need.
Pseudocode:
//Retrieve data from MySQL server
db.Select(category, productID);
//populate List<string>[] array
list[0] = db.ListQuery[0];
list[1] = db.ListQuery[1];
//convert list[] to Datatable
.....
Any help is much appreciated.
If I'm understanding your question right, do you mean something like this?
string category = "Category";
string productId = "ProductId";
List<string[]> tempList = db.Select(category, productID); //Not necessarily correct (I'm not familiar with MySQL). Do what you need to do to create the List<string[]>
DataTable table = new DataTable();
DataRow row;
table.Columns.Add(category);
table.Columns.Add(productId);
foreach (string[] s in tempList)
{
row = table.NewRow();
row[category] = s[0];
row[productId] = s[1];
table.Rows.Add(row);
}
DataTable dataTable = new DataTable();
List<MemberInfo> props = typeof(T).GetFields().Select(objField => (MemberInfo)objField).ToList();
props.AddRange(typeof(T).GetProperties().Select(objField => (MemberInfo)objField));
if (props.Count > 0)
{
Type t;
bool tIsField = false;
for (int iCnt = 0; iCnt < props.Count; iCnt++)
{
var prop = props[iCnt];
tIsField = prop.MemberType == MemberTypes.Field;
dataTable.Columns.Add(prop.Name, tIsField ? ((FieldInfo)prop).FieldType : ((PropertyInfo)prop).PropertyType);
}
foreach (T item in data)
{
DataRow dr = dataTable.NewRow();
foreach (var field in props)
{
tIsField = field.MemberType == MemberTypes.Field;
object value = tIsField ? ((FieldInfo)field).GetValue(item) : ((PropertyInfo)field).GetValue(item, null);
dr[field.Name] = value;
}
dataTable.Rows.Add(dr);
}
}
return dataTable;
I am using .NET 3.5 and need to convert the below select new result into a DataTable. Is there something built in for this or anyone know of a method that can do this?
var contentList = (from item in this.GetData().Cast<IContent>()
select new
{
Title = item.GetMetaData("Title"),
Street = item.GetMetaData("Street"),
City = item.GetMetaData("City"),
Country = item.GetMetaData("Country")
});
Easy and straightforward thing to do is to use reflection:
var records = (from item in this.GetData().Cast<IContent>()
select new
{
Title = "1",
Street = "2",
City = "3",
Country = "4"
});
var firstRecord = records.First();
if (firstRecord == null)
return;
var infos = firstRecord.GetType().GetProperties();
DataTable table = new DataTable();
foreach (var info in infos) {
DataColumn column = new DataColumn(info.Name, info.PropertyType);
table.Columns.Add(column);
}
foreach (var record in records) {
DataRow row = table.NewRow();
for (int i = 0; i < table.Columns.Count; i++)
row[i] = infos[i].GetValue(record);
table.Rows.Add(row);
}
Code may not be working up front but should give you a general idea. First, you get propertyInfos from anonymous type and use this metadata to create datatable schema (fill columns). Then you use those infos to get values from every object.
Here is one generic solution without Reflecting over the properties. Have an extension method as below
public static DataTable ConvertToDataTable<TSource>(this IEnumerable<TSource>
records, params Expression<Func<TSource, object>>[] columns)
{
var firstRecord = records.First();
if (firstRecord == null)
return null;
DataTable table = new DataTable();
List<Func<TSource, object>> functions = new List<Func<TSource, object>>();
foreach (var col in columns)
{
DataColumn column = new DataColumn();
column.Caption = (col.Body as MemberExpression).Member.Name;
var function = col.Compile();
column.DataType = function(firstRecord).GetType();
functions.Add(function);
table.Columns.Add(column);
}
foreach (var record in records)
{
DataRow row = table.NewRow();
int i = 0;
foreach (var function in functions)
{
row[i++] = function((record));
}
table.Rows.Add(row);
}
return table;
}
And Invoke the same using where parameters will be the column name in the order you want.
var table = records.ConvertToDataTable(
item => item.Title,
item => item.Street,
item => item.City
);
You can convert your list result to datatable by the below function
public static DataTable ToDataTable<T>(IEnumerable<T> values)
{
DataTable table = new DataTable();
foreach (T value in values)
{
if (table.Columns.Count == 0)
{
foreach (var p in value.GetType().GetProperties())
{
table.Columns.Add(p.Name);
}
}
DataRow dr = table.NewRow();
foreach (var p in value.GetType().GetProperties())
{
dr[p.Name] = p.GetValue(value, null) + "";
}
table.Rows.Add(dr);
}
return table;
}
There is a CopyToDataTable extension method which does that for you. It lives in System.Data.DataSetExtensions.dll
Try this:
// Create your datatable.
DataTable dt = new DataTable();
dt.Columns.Add("Title", typeof(string));
dt.Columns.Add("Street", typeof(double));
// get a list of object arrays corresponding
// to the objects listed in the columns
// in the datatable above.
var result = from item in in this.GetData().Cast<IContent>()
select dt.LoadDataRow(
new object[] { Title = item.GetMetaData("Title"),
Street = item.GetMetaData("Street"),
},
false);
// the end result will be a set of DataRow objects that have been
// loaded into the DataTable.
Original Article for code sample : Converting Anonymous type generated by LINQ to a DataTable type
EDIT: Generic Pseudocode:
void LinqToDatatable(string[] columns, Type[] datatypes, linqSource)
{
for loop
{
dt.columns.add(columns[i], datatypes[i]);
}
//Still thinking how to make this generic..
var result = from item in in this.GetData().Cast<IContent>()
select dt.LoadDataRow(
new object[] { string[0] = item.GetMetaData[string[0]],
string[1] = item.GetMetaData[srring[1]
},
false);
}
public static DataTable ListToDataTable<T>(this IList<T> data)
{
DataTable dt = new DataTable();
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
for (int i = 0; i < props.Count; i++)
{
PropertyDescriptor prop = props[i];
dt.Columns.Add(prop.Name, prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T t in data)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(t);
}
dt.Rows.Add(values);
}
return dt;
}
After you do your select new you can to .ToList().ListToDataTable(). This uses ComponentModel reflection and is (theroetically) faster than System.Reflection.
My given problem is follow:
I have an object with x columns and every column has y values. I must now bring this into Excel.
I found a snippet in which a datatable can be exported easily. So I will bring my object to a datatable. How can I do this?
Language is C#
I'm not completely certain I know what you're trying to do. I assume you want to create a DataTable and load your existing object into it. Assuming your class looks something like this:
public class MyClass {
public int ID {get;set;}
public string Column1 {get;set;}
public DateTime Column2 {get;set;}
// ...
}
and assuming you have a list of them you want to copy into a DataTable, here's how:
DataTable dt = new DataTable("MyTable");
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("Column1", typeof(string));
dt.Columns.Add("Column2", typeof(DateTime));
foreach (var o in _myObjectList) {
DataRow dr = dt.NewRow();
dr["ID"] = o.ID;
dr["Column1"] = o.Column1;
dr["Column2"] = o.Column2;
dt.Rows.Add(dr);
}
You can use reflection to get the fields of the object and add the columns to the DataTable:
private bool IsNullableType(Type theType)
{
return (theType.IsGenericType && theType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
}
// Create the columns based on the data in the album info - get by reflection
var ai = new <your object without data>;
Type t = ai.GetType();
this.dataTable.TableName = t.Name;
foreach (PropertyInfo p in t.GetProperties())
{
var columnSpec = new DataColumn();
// If nullable get the underlying type
Type propertyType = p.PropertyType;
if (IsNullableType(propertyType))
{
var nc = new NullableConverter(propertyType);
propertyType = nc.UnderlyingType;
}
columnSpec.DataType = propertyType;
columnSpec.ColumnName = p.Name;
this.dataTable.Columns.Add(columnSpec);
}
this.dataGridView.DataSource = dataTable;
Then to add a row to the table:
var info = new <your object with data>;
// Add by reflection
Type t = info.GetType();
var row = new object[t.GetProperties().Length];
int index = 0;
foreach (PropertyInfo p in t.GetProperties())
{
row[index++] = p.GetValue(info, null);
}
this.dataTable.Rows.Add(row);
How to 'union' 2 or more DataTables in C#?
Both table has same structure.
Is there any build-in function or should we do manually?
You are looking most likely for the DataTable.Merge method.
Example:
private static void DemonstrateMergeTable()
{
DataTable table1 = new DataTable("Items");
// Add columns
DataColumn idColumn = new DataColumn("id", typeof(System.Int32));
DataColumn itemColumn = new DataColumn("item", typeof(System.Int32));
table1.Columns.Add(idColumn);
table1.Columns.Add(itemColumn);
// Set the primary key column.
table1.PrimaryKey = new DataColumn[] { idColumn };
// Add RowChanged event handler for the table.
table1.RowChanged += new
System.Data.DataRowChangeEventHandler(Row_Changed);
// Add ten rows.
DataRow row;
for (int i = 0; i <= 9; i++)
{
row = table1.NewRow();
row["id"] = i;
row["item"] = i;
table1.Rows.Add(row);
}
// Accept changes.
table1.AcceptChanges();
PrintValues(table1, "Original values");
// Create a second DataTable identical to the first.
DataTable table2 = table1.Clone();
// Add column to the second column, so that the
// schemas no longer match.
table2.Columns.Add("newColumn", typeof(System.String));
// Add three rows. Note that the id column can't be the
// same as existing rows in the original table.
row = table2.NewRow();
row["id"] = 14;
row["item"] = 774;
row["newColumn"] = "new column 1";
table2.Rows.Add(row);
row = table2.NewRow();
row["id"] = 12;
row["item"] = 555;
row["newColumn"] = "new column 2";
table2.Rows.Add(row);
row = table2.NewRow();
row["id"] = 13;
row["item"] = 665;
row["newColumn"] = "new column 3";
table2.Rows.Add(row);
// Merge table2 into the table1.
Console.WriteLine("Merging");
table1.Merge(table2, false, MissingSchemaAction.Add);
PrintValues(table1, "Merged With table1, schema added");
}
private static void Row_Changed(object sender,
DataRowChangeEventArgs e)
{
Console.WriteLine("Row changed {0}\t{1}", e.Action,
e.Row.ItemArray[0]);
}
private static void PrintValues(DataTable table, string label)
{
// Display the values in the supplied DataTable:
Console.WriteLine(label);
foreach (DataRow row in table.Rows)
{
foreach (DataColumn col in table.Columns)
{
Console.Write("\t " + row[col].ToString());
}
Console.WriteLine();
}
}
You could try this:
public static DataTable Union (DataTable First, DataTable Second)
{
//Result table
DataTable table = new DataTable("Union");
//Build new columns
DataColumn[] newcolumns = new DataColumn[First.Columns.Count];
for(int i=0; i < First.Columns.Count; i++)
{
newcolumns[i] = new DataColumn(
First.Columns[i].ColumnName, First.Columns[i].DataType);
}
table.Columns.AddRange(newcolumns);
table.BeginLoadData();
foreach(DataRow row in First.Rows)
{
table.LoadDataRow(row.ItemArray,true);
}
foreach(DataRow row in Second.Rows)
{
table.LoadDataRow(row.ItemArray,true);
}
table.EndLoadData();
return table;
}
From here (not tested).
You could use Concat from Linq to datasets (get the free chapter of LINQ in Action) to join them and then .AsDataTable to create the table (assuming you actually want them as a DataTable)
Stumbled across this question, and Ruben Bartelink gave a great answer, but with no code. So I had to look it up elsewhere, which defeats the point of StackOverflow. Now that it's 2010, the other answers given aren't quite as viable. For reference, here's code demonstrating the CopyToDataTable() extension method. It's in VB so as to not steal the credit from Ruben if he wants to revisit the past and post a more complete answer :)
Public Function GetSchema(ByVal dbNames As IEnumerable(Of String)) As DataTable
Dim schemaTables As New List(Of DataTable)()
For Each dbName As String In dbNames
Dim cnnStr = GetConnectionString(dbName)
Dim cnn As New SqlConnection(cnnStr)
cnn.Open()
Dim dt = cnn.GetSchema("Columns")
cnn.Close()
schemaTables.Add(dt)
Next
Dim dtResult As DataTable = Nothing
For Each dt As DataTable In schemaTables
If dtResult Is Nothing Then
dtResult = dt
Else
dt.AsEnumerable().CopyToDataTable(dtResult, LoadOption.PreserveChanges)
End If
Next
Return dtResult
End Function
Try this using Linq to DataSet, must add the reference for System.Data.DataSetExtensions.dll, another approach, alternative for DataTable.Merge method).
static void Main(string[] args)
{
DoUnion();
}
private static void DoUnion()
{
DataTable table1 = GetProducts();
DataTable table2 = NewProducts();
var tbUnion = table1.AsEnumerable()
.Union(table2.AsEnumerable());
DataTable unionTable = table1.Clone();
foreach (DataRow fruit in tbUnion)
{
var fruitValue = fruit.Field<string>(0);
Console.WriteLine("{0}->{1}", fruit.Table, fruitValue);
DataRow row = unionTable.NewRow();
row.SetField<string>(0, fruitValue);
unionTable.Rows.Add(row);
}
}
private static DataTable NewProducts()
{
DataTable table = new DataTable("CitricusTable");
DataColumn col = new DataColumn("product", typeof(string));
table.Columns.Add(col);
string[] citricusFruits = { "Orange", "Grapefruit", "Lemon", "Lime", "Tangerine" };
foreach (string fruit in citricusFruits)
{
DataRow row = table.NewRow();
row.SetField<string>(col, fruit);
table.Rows.Add(row);
}
return table;
}
private static DataTable GetProducts()
{
DataTable table = new DataTable("MultipleFruitsTable");
DataColumn col = new DataColumn("product", typeof(string));
table.Columns.Add(col);
string[] multipleFruits = { "Breadfruit", "Custardfruit", "Jackfruit", "Osage-orange", "Pineapple" };
foreach (string fruit in multipleFruits)
{
DataRow row = table.NewRow();
row.SetField<string>(col, fruit);
table.Rows.Add(row);
}
return table;
}
antonio