merge two datatable arrays C# - c#

i have two datatable arrays
DataTable[] DTrightSplitH2;
DataTable[] DTleftSplitH2;
what i try to do is to take each datatable in DTright and compare to DTleft in "key" columns
it they are the same, merge the row
i know i should use DataTable.Merge with bool set to false and adding missing schema but i can't make it work like i want

Try this :
DTrightSplitH2.Union(DTleftSplitH2);

Sounds as if you could use my MergeAll method (usage below).
public static DataTable MergeAll(this IList<DataTable> tables, String primaryKeyColumn)
{
if (!tables.Any())
throw new ArgumentException("Tables must not be empty", "tables");
if(primaryKeyColumn != null)
foreach(DataTable t in tables)
if(!t.Columns.Contains(primaryKeyColumn))
throw new ArgumentException("All tables must have the specified primarykey column " + primaryKeyColumn, "primaryKeyColumn");
if(tables.Count == 1)
return tables[0];
DataTable table = new DataTable("TblUnion");
table.BeginLoadData(); // Turns off notifications, index maintenance, and constraints while loading data
foreach (DataTable t in tables)
{
foreach (DataColumn col in t.Columns)
col.ReadOnly = false; // required e.g. if you use a DataSet with Foreign-Key Constraints
table.Merge(t); // same as table.Merge(t, false, MissingSchemaAction.Add);
}
table.EndLoadData();
if (primaryKeyColumn != null)
{
// since we might have no real primary keys defined, the rows now might have repeating fields
// so now we're going to "join" these rows ...
var pkGroups = table.AsEnumerable()
.GroupBy(r => r[primaryKeyColumn]);
var dupGroups = pkGroups.Where(g => g.Count() > 1);
foreach (var grpDup in dupGroups)
{
// use first row and modify it
DataRow firstRow = grpDup.First();
foreach (DataColumn c in table.Columns)
{
if (firstRow.IsNull(c))
{
DataRow firstNotNullRow = grpDup.Skip(1).FirstOrDefault(r => !r.IsNull(c));
if (firstNotNullRow != null)
firstRow[c] = firstNotNullRow[c];
}
}
// remove all but first row
var rowsToRemove = grpDup.Skip(1);
foreach(DataRow rowToRemove in rowsToRemove)
table.Rows.Remove(rowToRemove);
}
}
return table;
}
Usage:
var tables = DTrightSplitH2.Concat(DTleftSplitH2).ToArray();
DataTable TblUnion = tables.MergeAll("key");

Related

How to foreach through a list of datatables?

I have a list of 2 DataTables. I want to iterate through each of them, one at a time. How do I do this? There are 0 examples of going through a list of DataTables.
List<DataTable> test = new List<DataTable>();
foreach (DataRow dataRow in TEST.LIST)
{
string value = dataRow.Field<string>("Slave_IO_Running"); //Looks for "Slave_IO_Running" status.
if (value == "Yes")
{
results.Add(siteName + ": WORKING"); //adds working to the visual table
}
else
{
results.Add(siteName + ": REPLICATION ERROR"); //adds not working to the result list
}
break;
}
for each datatable you can use DataTable.Rows and for each row you can access the properties as row["columnName"] or traverse each column in the corresponding row
like
foreach(DataTable table in tables)
{
foreach(DataRow row in table.Rows)
{
foreach(DataColumn column in table.Columns)
{
Console.WriteLine(row[column]);
}
}
}
You can try it like this, assuming both tables contain the same columns:
foreach (DataRow dataRow in test.SelectMany(dt => dt.Rows.OfType<DataRow>()))
{
// your code using the rows
}

DataTable columns in C#

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;
}

C# DataGridView, show columns that only have data

I am using a datagridview to mirror my sql table using linq so:
var listOfCarRecords = dataGridView1.DataSource = record.Select(x => Mapper.Map<Cars>(x)).ToList();
This works fine.
However my sql table has 60 columns of which only a few are ever contain data, there are few occassions when all contain data.
So how can I show only columns with data in my datagridview?
I tried setting autogeneratecolumns = falsebut it did not work.
whats the simplest way to do this please?
thank you
Before assigning Datatable as datasource, you can remove the columns which have null values.
if (table.AsEnumerable().All(dr => dr.IsNull("ColumnName"))) // column name having null values
table.Columns.Remove("ColumnName");
and then assign it to your dataGridview
datagridview1.DataSource = table;
EDIT :
DataTable dataTable = record.Select(...) // Your data coming from DB
// Remove Empty Columns
if (dataTable.AsEnumerable().All(dr => dr.IsNull("ColumnName"))) // column name having null values
dataTable.Columns.Remove("ColumnName");
List<Cars> listOfCarRecords = dataTable.ToCollection<Cars>;
dataGridView1.DataSource = listOfCarRecords;
// ToCollection is an extension methods for converting generic class to List
public static List<T> ToCollection<T>(this DataTable sourceDatatable)
{
var lst = new List<T>();
Type tClass = typeof(T);
PropertyInfo[] pClass = tClass.GetProperties();
List<DataColumn> dc = sourceDatatable.Columns.Cast<DataColumn>().ToList();
foreach (DataRow item in sourceDatatable.Rows)
{
var genericObject = (T)Activator.CreateInstance(tClass);
foreach (PropertyInfo pc in pClass)
{
DataColumn d = dc.Find(c => c.ColumnName == pc.Name);
if (d != null)
pc.SetValue(genericObject, item[pc.Name], null);
}
lst.Add(genericObject);
}
return lst;
}

How to assign a value to the original datarow after I am iterating over a temporary datatable

I am trying to explain what I need to do.
As you can see in the second foreach, I am iterating over the temporary data table, but I need to set a value for the same row in the original data tablerow.
For example:
_uc090_WingsIntegrationDataSet.WingsBookingInterface[0]["property"] = x;
What I dont know how to implement is how to find that row and set the property, I saw the LoadRow method but I never used it before.
DataTable tempTable = _uc090_WingsIntegrationDataSet.WingsBookingInterface.Clone();
DataRow[] datarows = _uc090_WingsIntegrationDataSet.WingsBookingInterface.Select("REFMDossierID = " + refmDossierId);
if (datarows.Length > 0)
{
foreach (DataRow dr in datarows)
{
tempTable.ImportRow(dr);
}
}
//2. foreach master row
foreach (UC090_WingsIntegrationDataSet.WingsBookingInterfaceRow row in tempTable.Rows)
You can find the row using Rows.Find(), but it requires that a PrimaryKey be set on at least one column in your DataTable.
As far as loading new data, you can use LoadDataRow() which will update existing rows (if a primary key is supplied) or insert new data if any matching datatypes are found.
Please take a look at the following example using untyped datasets:
DataSet dataSet = new DataSet("MyDataSet");
DataTable dataTable = dataSet.Tables.Add("JavaScriptLibraries");
DataColumn[] dataColumns =
new[] {
new DataColumn("Id", typeof(Int32))
{
AutoIncrement = true,
AllowDBNull = false,
AutoIncrementSeed = 1
},
new DataColumn("Name", typeof(String))
};
dataTable.Columns.AddRange(dataColumns);
dataTable.PrimaryKey = new[] { dataTable.Columns["Id"] };
DataRow dataRow1 = dataTable.NewRow();
dataRow1["Name"] = "jQuery";
dataTable.Rows.Add(dataRow1);
DataRow dataRow2 = dataTable.NewRow();
dataRow2["Name"] = "MooTools";
dataTable.Rows.Add(dataRow2);
// Copy the dataset
DataSet tempDataSet = dataSet.Clone();
DataTable tempDataTable = tempDataSet.Tables["JavaScriptLibraries"];
DataRow[] tempRows = dataSet.Tables["JavaScriptLibraries"].Select("Name = 'jQuery'");
// Import rows to copy of table
foreach (var tempRow in tempRows)
{
tempDataTable.ImportRow(tempRow);
}
foreach (DataRow tempRow in tempDataTable.Rows)
{
// Find existing row by PK, then update it
DataRow originalRow = dataTable.Rows.Find(tempRow["Id"]);
originalRow["Name"] = "Updated Name";
}
// Load new data using LoadDataRow()
object[] newRow = new[] { null, "New Row" };
dataTable.BeginLoadData();
dataTable.LoadDataRow(newRow, true);
dataTable.EndLoadData();

How to build a DataTable from a DataGridView?

I may well be looking at this problem backwards but I am curious none the less. Is there a way to build a DataTable from what is currently displayed in the DataGridView?
To be clear, I know you can do this DataTable data = (DataTable)(dgvMyMembers.DataSource); however that includes hidden columns. I would like to build it from the displayed columns only.
Hope that makes sense.
So I ended up trying a combination of a couple of answers as that seemed best. Below is what I am trying. Basically I am creating the DataTable from the DataSource and then working backwards based on if a column is visible or not. However, after it removes a column I get a Collection was modified; enumeration operation may not execute on the next iteration of the foreach.
I am confused as I am not trying to modify the DataGridView, only the DataTable so what's up?
DataTable data = GetDataTableFromDGV(dgvMyMembers);
private DataTable GetDataTableFromDGV(DataGridView dgv)
{
var dt = ((DataTable)dgv.DataSource).Copy();
foreach (DataGridViewColumn column in dgv.Columns)
{
if (!column.Visible)
{
dt.Columns.Remove(column.Name);
}
}
return dt;
}
Well, you can do
DataTable data = (DataTable)(dgvMyMembers.DataSource);
and then use
data.Columns.Remove(...);
I think it's the fastest way. This will modify data source table, if you don't want it, then copy of table is reqired. Also be aware that DataGridView.DataSource is not necessarily of DataTable type.
I don't know anything provided by the Framework (beyond what you want to avoid) that would do what you want but (as I suspect you know) it would be pretty easy to create something simple yourself:
private DataTable GetDataTableFromDGV(DataGridView dgv) {
var dt = new DataTable();
foreach (DataGridViewColumn column in dgv.Columns) {
if (column.Visible) {
// You could potentially name the column based on the DGV column name (beware of dupes)
// or assign a type based on the data type of the data bound to this DGV column.
dt.Columns.Add();
}
}
object[] cellValues = new object[dgv.Columns.Count];
foreach (DataGridViewRow row in dgv.Rows) {
for (int i = 0; i < row.Cells.Count; i++) {
cellValues[i] = row.Cells[i].Value;
}
dt.Rows.Add(cellValues);
}
return dt;
}
one of best solution enjoyed it ;)
public DataTable GetContentAsDataTable(bool IgnoreHideColumns=false)
{
try
{
if (dgv.ColumnCount == 0) return null;
DataTable dtSource = new DataTable();
foreach (DataGridViewColumn col in dgv.Columns)
{
if (IgnoreHideColumns & !col.Visible) continue;
if (col.Name == string.Empty) continue;
dtSource.Columns.Add(col.Name, col.ValueType);
dtSource.Columns[col.Name].Caption = col.HeaderText;
}
if (dtSource.Columns.Count == 0) return null;
foreach (DataGridViewRow row in dgv.Rows)
{
DataRow drNewRow = dtSource.NewRow();
foreach (DataColumn col in dtSource .Columns)
{
drNewRow[col.ColumnName] = row.Cells[col.ColumnName].Value;
}
dtSource.Rows.Add(drNewRow);
}
return dtSource;
}
catch { return null; }
}
First convert you datagridview's data to List, then convert List to DataTable
public static DataTable ToDataTable<T>( this List<T> list) where T : class {
Type type = typeof(T);
var ps = type.GetProperties ( );
var cols = from p in ps
select new DataColumn ( p.Name , p.PropertyType );
DataTable dt = new DataTable();
dt.Columns.AddRange(cols.ToArray());
list.ForEach ( (l) => {
List<object> objs = new List<object>();
objs.AddRange ( ps.Select ( p => p.GetValue ( l , null ) ) );
dt.Rows.Add ( objs.ToArray ( ) );
} );
return dt;
}

Categories