How to reduce the amount of objects created parsing dataset? - c#

Dataset consists of a table of two columns. Table name=Project. One column name=Name, other column name=Resource. There are multiple repetitive project names.
var dataset = FakeDataset.CreateDataset();
var projectList = new List<Project>();
foreach (DataTable table in dataset.Tables)
{
foreach (DataRow dataRow in table.Rows)
{
projectList.Add(new Project { Name = Convert.ToString(dataRow["Name"]), Resource = Convert.ToString(dataRow["Resource"]) });
}
}
Now I am creating a Project object every time for a single project Name in the dataset. What I want is - to create Project object only for unique project Name in dataset. I am a beginner so would be nice a simple solution.

Not tested and I'm not sure if I understood you correctly, but from my assumptions
you want something like this:
var dataset = FakeDataset.CreateDataset();
var projectList = new List<Project>();
foreach (DataTable table in dataset.Tables)
{
foreach (DataRow dataRow in table.Rows)
{
if(projectList.Any(Project => Project.Name == dataRow["Name"]))
projectList.Add(new Project { Name = Convert.ToString(dataRow["Name"]), Resource = Convert.ToString(dataRow["Resource"]) });
}
}
Edit:
var dataset = FakeDataset.CreateDataset();
var projectList = new List<Project>();
foreach (DataTable table in dataset.Tables)
{
foreach (DataRow dataRow in table.Rows)
{
if (!projectList.Any(Project => Project.Name == Convert.ToString(dataRow["Name"])))
{
projectList.Add(new Project { Name = Convert.ToString(dataRow["Name"]), Resource = Convert.ToString(dataRow["Resource"]) });
}
else
{
Project p = projectList.Find(Project => Project.Name == Convert.ToString(dataRow["Name"]));
projectList[projectList.IndexOf(p)].Resource += "\r\n" + Convert.ToString(dataRow["Name"]);
}
}
}

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

Running through a DataRow, can get the column values but not the header?

DataTable currentAttribs = //return dataTable of results;
foreach (DataRow r in currentAttribs.Rows)
{
foreach (DataColumn column in r.ItemArray)
{
//run through dataRow and access header?????
{
tableRow = "<TR><TD>" + column[0].ToString() + "</TD></TR>";
Literal lc = new Literal();
lc.Text = tableRow;
divFeatureInfo.Controls.Add(lc);
}
}
}
Returns all the values in the column, but I can't seem to access the value of column header
I can see the header stepping through but do I need to acces it from the outerloop?
UPDATE
I can view the header title from here - r.Table.Columns.NonPublicMembers.List();..but how do i access each one?
Shouldnt it be done inside the r.itemArray and not currentAttribs.rows
Loop through the columns
r.Table.Columns.Item(i)
r.Table.Columns.Item(i).Caption
It can be achieved by using Table property of your DataRow instance.
foreach (DataColumn c in r.Table.Columns) //loop through the columns.
{
MessageBox.Show(c.ColumnName);
}
It can be used as
DataTable currentAttribs = //return dataTable of results;
foreach (DataRow r in currentAttribs.Rows)
{
foreach (DataColumn column in currentAttribs.Columns)
{
//run through dataRow and access header?????
{
tableRow = "<TR><TD>" + column.ColumnName + "</TD></TR>";
Literal lc = new Literal();
lc.Text = tableRow;
divFeatureInfo.Controls.Add(lc);
}
}
}
It's not a perfect answer to your specific question, but it an answer to the question itself of "How can I map up the ColumnHeaders with the DataRow's ItemArray Values.
For me, this is the basic solution.
private void UpdateButton_Click(object sender, EventArgs e)
{
DataRow[] modifiedRows = _dataTable.Select("", "", DataViewRowState.ModifiedCurrent);
foreach (var row in modifiedRows)
{
for (var i = 0; i < row.Table.Columns.Count; i++)
{
var ColumnName = row.Table.Columns[i].ColumnName;
var ColumnValue = row.ItemArray[i];
}
//... build objects now that we can map the property names and new values...
}
}

adding in SortedDictionary

I want to add table name as string and table value as arraylist if table name exist in same key add record as array list ...here showing key already exist . where i can modify :
SortedDictionary<string, ArrayList> DealInfo = new SortedDictionary<string, ArrayList>();
ArrayList Deal = new ArrayList();
string DealName=string.Empty;
foreach (DataTable table in RecentAddedDeal.Tables)
{
foreach (DataRow dr in table.Rows)
{
if (!DealInfo.ContainsKey(Convert.ToString(dr["DealTab"])))
{
DealName = Convert.ToString(dr["DealTab"]);
}
Deal.Add(dr);
DealInfo.Add(DealName, Deal);
}
}
It's hard to tell exactly what you want since your description says you want to use the TableName as the key, but your code is adding a column from the row as a key.
Going with the idea that the TableName should be the key, something like this should work:
var dealInfo = new SortedDictionary<string, List<DataRow>>();
foreach (DataTable table in RecentAddedDeal.Tables)
{
if (!dealInfo.ContainsKey(table.TableName))
{
dealInfo.Add(table.TableName, table.Rows.Cast<DataRow>().ToList());
}
}
Going with the idea that you want to key off a column in each row for the key, you could do something like:
var dealInfo = new SortedDictionary<string, List<DataRow>>();
foreach (DataTable table in RecentAddedDeal.Tables)
{
foreach (DataRow row in table.Rows)
{
var dealName = row["DealTab"].ToString();
if (dealInfo.ContainsKey(dealName))
{
dealInfo[dealName].Add(row);
}
else
{
dealInfo.Add(dealName, new List<DataRow> {row});
}
}
}
To fill a ListView with data from a row for a specific DealName, you can find the dictionary entry for that deal name and access the list of rows this way:
foreach (DataRow row in dealInfo["SomeDealName"])
{
// Here you have access to the rows where row["DealTab"] == "SomeDealName"
// You can fill a list view with some column value from the row like:
listView1.Add(row["SomeColumnName"].ToString());
}

merge two datatable arrays 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");

Categories