Make a copy of a Datatable with some changes - c#

I am trying to make a copy of a DataTable dt1 to a new one dt2.
dt1 contains columns of string and boolean types.
dt2 will contains only string types.
The following is my code which works fine.
public DataTable Convert(DataTable dt1)
{
try
{
var dt2 = dt1.Clone();
foreach (DataColumn dc in dt2.Columns)
{
dc.DataType = Type.GetType("System.String");
}
foreach (DataRow row in dt1.Rows)
{
dt2.ImportRow(row);
}
foreach (DataRow dr in dt2.Rows)
{
foreach (DataColumn dc in dt2.Columns)
{
bool value;
if (bool.TryParse(dr[dc].ToString(), out value))
{
dr[dc] = "+";
}
}
}
return dt2;
}
finally
{
}
}
1st step: Clone of dt1 and change the columns types to string.
2nd step: Import the rows from dt1 to dt2
3rd step: Change all true values to "+"
Is there a better way to perform those steps. "better" mean clearer, simpler, less code, less steps, better performance.

The code you've provided very clearly describes your intent and is succinct. We can however make some optimizations that will help with performance.
You are attempting to parse every field of every row instead of determining which fields actually require manipulation. As the number of non-boolean columns increases you're doing more work than is needed.
You already have the original table which contains bool values so you could do away with parsing all together.
Here is a version that optimizes around these two points. The code is not shorter or more elegant, but it will require no text parsing and far fewer evaluations.
public DataTable Convert2(DataTable dt1)
{
DataTable dt2 = dt1.Clone();
// Alter all columns datatype
foreach (DataColumn col in dt2.Columns)
col.DataType = typeof(string);
// Import all rows from existing table
foreach (DataRow row in dt1.Rows)
dt2.ImportRow(row);
// Index the boolean columns that will require evaluation
List<int> booleans = new List<int>();
foreach (DataColumn col in dt1.Columns)
{
if (col.DataType == typeof(bool))
booleans.Add(col.Ordinal);
}
// Since two tables will be identical except for datatypes
// iterate over original table and cast instead of performing
// a string conversion and parsing the result.
for (int row = 0; row < dt1.Rows.Count; row++)
{
foreach (int index in booleans)
{
if ((bool) dt1.Rows[row][index])
dt2.Rows[row][index] = "+";
}
}
return dt2;
}
Additionally, I left out the try..finally block since you're not using it.

That code would do the job.
public DataTable Convert(DataTable dt1)
{
try
{
var dt2 = dt1.Clone();
foreach (DataColumn dc in dt2.Columns)
{
dc.DataType = Type.GetType("System.String");
}
foreach (DataRow row in dt1.Rows)
{
dt2.ImportRow(row);
DataRow dr = dt2.Rows[dt2.Rows.Count-1]
foreach (DataColumn dc in dt2.Columns)
{
bool value;
if (bool.TryParse(dr[dc].ToString(), out value))
{
dr[dc] = "+";
}
}
}
return dt2;
}
finally
{
}
}

Related

collection was modified enumeration operation might not execute

string field = ViewState["Field"].ToString();
DataTable dt = (DataTable)Session["Academic"];
foreach (DataRow dr in dt.Rows)
{
if (dr["Degree"].ToString() == field)
{
dr.Delete();
dt.AcceptChanges();
}
}
Session["Academic"] = dt;
gdvwAcademic1.DataSource = Session["Academic"] as DataTable;
gdvwAcademic1.DataBind();
when this code executed raise error as "collection was modified enumeration operation might not execute." why this so..?
You cannot modify a collection in a foreach. Try this as an alternative to apomene's answer (Pretty much does the same thing, except using the remove method of a list instead of indexes.
List<DataRow> toDelete = new List<DataRow>();
foreach(DataRow dr in dt.Rows){
if(dr["Degree"].ToString() == field){
toDelete.Add(dr);
}
}
foreach (DataRow dr in toDelete){
dt.Rows.Remove(dr);
}
This should solve your problem.
You can't modify the collection in a foreach. So don't delete or add things in it.
You could prevent this error by creating a new collection with items you want to modify, for example with LINQ (the ToList() is essential):
var toDelete = dt.AsEnumerable().Where(r => r["Degree"].ToString() == field).ToList();
Now you can loop this without a problem.
foreach(DataRow rowToDelete in toDelete)
rowToDelete.Delete();
dt.AcceptChanges(); // you could do that also in the loop now
you cant delete inside foreach use for loop or make a delete List:
List <int> toBeDel=new List<int>();
foreach (DataRow dr in dt.Rows)
{
if (dr["Degree"].ToString() == field)
{
toBEDel.Add(indexOf(dr));
}
}
foreach (int i in toBeDel)
{
dt.Rows[i].Delete();
}
for (int i = 0;i < dt.Rows.Count; i++)
{
DataRow dr = dt.Rows[i];
if (dr["Degree"].ToString() == field)
{
dr.Delete();
i-=1;
}
}
dt.AcceptChanges();
you can use this, one loop.
For any people find this post.

Looping through 2 datatable and adding values to third

I have created 3 datatables
var dt1= new DataTable();
var dt2= new DataTable();
var dt3= new DataTable();
Then i loop
foreach (DataRow row1 in dt1.Rows)
{
dt3.Rows.Add(row1.ItemArray);
foreach (DataRow row2 in dt2.Rows)
{
var Id2 = row1["Id"];
var Id1= row2["Id"];
if (Id1 == Id2)
{
dt3.rows["Name"] = "" ; // doesnt work
}
}
}
As you can see that i loop on 2 datatables. Then in inner loop i check if the records matches. Now if the record matches then i want to update column "Name" on dt3 datatable.
I tried using
dt3.rows["Name"] = "" ;
But this doesnt work. I know the reason that its because i again need to loop on dt3 datatable and
assign values to column in that loop. But not sure how to do it and if there is even more better solution. I mean we can find id in dt3 datatable and then update value. But not sure how to do it
Is there more intelligent solution than looping on 2 table?
The first foreach is not needed. A simple DataTable.Copy will bring all the data and structure from the original table in the destination table. Then looping on the second table and Select on the third to find the matching rows and clear the name.
dt3 = dt1.Copy();
foreach (DataRow row2 in dt2.Rows)
{
DataRow[] match = dt3.Select("ID=" + row2["ID"].ToString());
if(match.Lenght > 0)
match[0]["Name"] = "" ;
}
Not sure if this is more performant from the other answers. Need to be tested
dt3.rows doesn't work, you want to change the name of the row that you have added now. This should work:
foreach (DataRow row1 in dt1.Rows)
{
DataRow newRow = dt3.Rows.Add(row1.ItemArray);
foreach (DataRow row2 in dt2.Rows)
{
var Id2 = row1["Id"];
var Id1 = row2["Id"];
if (Id1 == Id2)
{
newRow["Name"] = "new Name";
}
}
}
Try this:
foreach (DataRow row1 in dt1.Rows)
{
var row = dt3.Rows.Add(row1.ItemArray);
foreach (DataRow row2 in dt2.Rows)
{
var Id2 = row1["Id"];
var Id1= row2["Id"];
if (Id1 == Id2)
{
row["Name"] = ""; //maybe works
}
}
}
How about,
dt2Lookup = new HashSet(
dt2.AsEnumerable().Select(row => row.Field<int>("Id")));
dt3 = dt1.Clone();
forreach (var row In dt1.AsEnumerable())
{
var newRow = dt3.Rows.Add(row.ItemArray)
if (dt2lookup.Contains(row.Field<int>("Id"))
{
newRow.SetField("Name", string.Empty);
}
}
The HashSet should provide good lookup performance.
dt3 = dt1.Copy();
var RowDictionary = dt3.Rows.OfType<DataRow>().ToDictionary(dr => dr["ID"].ToString());
//replace by Dictionary<string,List<DataRow>> in case ID is not unique and fill it with a foreach loop.
foreach (DataRow row2 in dt2.Rows)
{
DataRow Match;
if (c.TryGetValue(row2["ID"].ToString(), out Match))
{
Match["Name"] = "";
}
}
You should call:
dt3.rows[dt3.rows.Count - 1].Columns["Name"] = "" ;

Delete Row From Data Table

I want to Delete the Multiple records from the DataTable
For example :
in my case PaperId is Repeating several Times.I want to Delete it all Duplicate records.
i have written code but loop is giving error
DataSet ds = new DataSet();
sqlDad.Fill(ds);
DataTable dt1 = new DataTable();
ds.Tables.Add(dt1);
dt1 = ds.Tables[0];
DataTable dt2 = new DataTable();
dt2 = dt1;
List<DataRow> rowsToDelete = new List<DataRow>();
foreach(DataRow dr in ds.Tables[0].Rows)
{
int r = ds.Tables[0].Columns.Count;
string x = dr.ItemArray[0].ToString();
int counter = 0;
foreach (DataRow dr1 in ds.Tables[0].Rows)
{
if (x == dr1.ItemArray[0].ToString())
{
counter++;
}
if (counter > 1)
{
rowsToDelete.Add(dr1);
foreach (DataRow row in rowsToDelete)
{
dt2.Rows.Remove(row);
}
dt2.AcceptChanges();
rowsToDelete.Clear();
}
}
Using the DefaultView of the DataTable and setting the sort order on the column that you don't want repeats to appear. You could loop over the rows and delete all the rows after the first one
// Work on the first table of the DataSet
DataTable dt1 = ds.Tables[0];
// No need to work if we have only 0 or 1 rows
if(dt1.Rows.Count <= 1)
return;
// Setting the sort order on the desidered column
dt1.DefaultView.Sort = dt1.Columns[0].ColumnName;
// Set an initial value ( I choose an empty string but you could set to something not possible here
string x = string.Empty;
// Loop over the row in sorted order
foreach(DataRowView dr in dt1.DefaultView)
{
// If we have a new value, keep it else delete the row
if(x != dr[0].ToString())
x = dr[0].ToString();
else
dr.Row.Delete();
}
// Finale step, remove the deleted rows
dt1.AcceptChanges();
Try This
DataRow[] rows;
rows=dataTable.Select("UserName = 'ABC'"); // UserName is Column Name
foreach(DataRow r in rows)
r.Delete();
If you want to remove the entire row from DataTable ,
try this
DataTable dt = new DataTable(); //User DataTable
DataRow[] rows;
rows = dt.Select("UserName = 'KarthiK'");
foreach (DataRow row in rows)
dt.Rows.Remove(row);

Looping through a DataTable

Well. I have a DataTable with multiple columns and multiple rows.
I want to loop through the DataTable dynamically basically the output should look as follows excluding the braces :
Name (DataColumn)
Tom (DataRow)
Peter (DataRow)
Surname (DataColumn)
Smith (DataRow)
Brown (DataRow)
foreach (DataColumn col in rightsTable.Columns)
{
foreach (DataRow row in rightsTable.Rows)
{
//output
}
}
I typed that out and noticed this would not work. Can someone please advice on a better way of doing this?
foreach (DataColumn col in rightsTable.Columns)
{
foreach (DataRow row in rightsTable.Rows)
{
Console.WriteLine(row[col.ColumnName].ToString());
}
}
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn col in dt.Columns)
Console.WriteLine(row[col]);
}
Please try the following code below:
//Here I am using a reader object to fetch data from database, along with sqlcommand onject (cmd).
//Once the data is loaded to the Datatable object (datatable) you can loop through it using the datatable.rows.count prop.
using (reader = cmd.ExecuteReader())
{
// Load the Data table object
dataTable.Load(reader);
if (dataTable.Rows.Count > 0)
{
DataColumn col = dataTable.Columns["YourColumnName"];
foreach (DataRow row in dataTable.Rows)
{
strJsonData = row[col].ToString();
}
}
}
If you want to change the contents of each and every cell in a datatable then we need to Create another Datatable and bind it as follows using "Import Row". If we don't create another table it will throw an Exception saying "Collection was Modified".
Consider the following code.
//New Datatable created which will have updated cells
DataTable dtUpdated = new DataTable();
//This gives similar schema to the new datatable
dtUpdated = dtReports.Clone();
foreach (DataRow row in dtReports.Rows)
{
for (int i = 0; i < dtReports.Columns.Count; i++)
{
string oldVal = row[i].ToString();
string newVal = "{"+oldVal;
row[i] = newVal;
}
dtUpdated.ImportRow(row);
}
This will have all the cells preceding with Paranthesis({)

Add columnname and data from datatable to a dictionary? C#

I am looping through a datatable and currently I do
string customerId = row["CustomerId"].ToString();
string companyName = row["Company Name"].ToString();
Instead of declaring every variable how do I add these do a dictionary?
I was thinking something like:
foreach (DataRow row in customerTbl.Rows)
{
Dictionary<string, string> customerDictionary = new Dictionary<string, string>();
customerDictionary.Add(row[].ToString(), row[].ToString());
so yeah, how do I get the row name and value into there?
Thanks in advance.
EDIT:
I guess I didn't give you all information about what I am trying to do.
I query a database for a customer and populate a datatable with about 50 columns, I use the customer id to query another database and get the same customer from there. I then want to compare all 50 fields from both databases for the same customer and check if any field is different. I thought I could add the column name with the value in a dictionary for each row for each customer and then with two loops check if the values for the same key (column name) differs, is this a totally wrong approach?
having an if (customerId1 != customerId2 || customerName1 != customerName2 || and so on for 50 fields is not very good.
Are you looking for a generic solution to this kind of thing? If so does your DataTable always contain 2 columns where the first can be treated as the dictionary key and the second the value? If so, then simply use the column index instead of the column name
var customerDictionary = new Dictionary<string, string>();
foreach (DataRow row in customerTbl.Rows)
customerDictionary.Add(row[0].ToString(), row[1].ToString());
If the values in the customerId and CompanyName columns are never null then instead of using ToString(), you can cast the values to a string
customerDictionary.Add((string)row[0], (string)row[1]);
FYI, the DataTable has a search capability as well (the Find() method). If you set up a primary key column the search will be extremely fast and you may not need to copy the data from a DataTable to a Dictionary.
Edit : A complete working example of one way of doing this in C# code as per the OPs edited question
class Program
{
static void Main(string[] args)
{
var table1 = GetCustomersFromFirst();
var table2 = GetCustomersFromSecond();
foreach (DataRow row in table1.Rows)
{
var foundRows = table2.Select("CustomerId = " + (string)row[0]);
if (foundRows.Length == 1)
{
var foundRow = foundRows[0];
foreach (DataColumn col in table1.Columns)
{
var valueOfColumnFromTable1 = row[col.Ordinal].ToString();
var valueOfColumnFromTable2 = foundRow[col.Ordinal].ToString();
if (String.Compare(valueOfColumnFromTable1, valueOfColumnFromTable2) != 0)
{
//the colum values are not the same.
}
}
}
else
{
// Something is wrong since more than one matching record was found
// or no matching records were found.
}
}
}
static DataTable GetCustomersFromFirst()
{
var dt = GetInitializedCustomerDataTable();
var row = dt.NewRow();
row[0] = "1";
row[1] = "CompanyA";
dt.Rows.Add(row);
row = dt.NewRow();
row[0] = "2";
row[1] = "CompanyB";
dt.Rows.Add(row);
row = dt.NewRow();
row[0] = "3";
row[1] = "CompanyC";
dt.Rows.Add(row);
return dt;
}
static DataTable GetCustomersFromSecond()
{
var dt = GetInitializedCustomerDataTable();
var row = dt.NewRow();
row[0] = "1";
row[1] = "CompanyA";
dt.Rows.Add(row);
row = dt.NewRow();
row[0] = "2";
row[1] = "CompanyD";
dt.Rows.Add(row);
row = dt.NewRow();
row[0] = "3";
row[1] = "CompanyC";
dt.Rows.Add(row);
return dt;
}
static DataTable GetInitializedCustomerDataTable()
{
var dt = new DataTable();
dt.Columns.Add("CustomerId", typeof(string));
dt.Columns.Add("CompanyName", typeof(string));
return dt;
}
}
In the test data, the 2nd row does not match. So it should fall into the condition where the comment says:
//the colum values are not the same.
Personally, I'd look at doing this in the database itself.
Try this.
Dictionary<string, string> customerDictionary = new Dictionary<string, string>();
foreach (DataRow row in customerTbl.Rows)
{
if(row["CustomerId"] != null && customerDictionay.ContainsKey("CustomerId") == false)
{
customerDictionary.Add(row["CustomerID"].ToString(), row["CompanyName"] as string);
}
}
Just a question, what's the benefit of putting the values in a dictionary? You are basically just eliminating the need of writing .ToString() when accessing the values...i.e, instead of:
row["CustomerID"].ToString();
you write
customerDictionary["CustomerID"];
Or have I missed something?

Categories

Resources