So i have a very simple function which goes through a table and find those rows that has the age columns between 2 numbers and then return a new table with the new rows. here is my function :
private DataTable AgeFinder(int MiniAge, int MaxiAge, DataTable x)
{
//AFWT : Analyze From Which Table
int count;
DataTable dtAge = new DataTable();
dtAge = x.Clone();
int ageMin = MiniAge;
int ageMax = MaxiAge;
if (ageMin > ageMax)
{
int temp = ageMax;
ageMax = ageMin;
ageMin = temp;
}
int ageDr;
count = 0;
ageDr = 0;
dataGridView1.DataSource = x;
foreach (DataRow dr in x.Rows)
{
ageDr = Convert.ToInt16(x.Rows[count]["age "]);
if (ageDr >= ageMin && ageDr < ageMax)
{
dtAge.Rows.Add(dr.ItemArray);
}
count++;
}
lblCountThisQuery.Text = dtAge.Rows.Count.ToString();
return dtAge;
}
and here is how i call it:
dtAge = AgeFinder(Convert.ToInt16(tbxMinAge.Text), Convert.ToInt16(tbxMaxAge.Text), AT);
dataGridView1.DataSource = dtAge;
now i can see that the AT is not empty but when i check the x (the table inside the method), its empty! why does this happen?
Related
I have a DataTable and I want to do some mathematical operations before adding them to DataGridView. First one, I want to find maximum number of third column of my DataTable, second one I want to divide all values in third column with this maximum number. After all I want to replace my new values instead old values. My DataTable looks like that;
Column 1 Column 2 Column 3
---------------------------------------
a b 2000000
q r 250000
s t 185000
m w 400000
o p 750000
After the operations, my DataTable should look like that;
Column 1 Column 2 Column 3
---------------------------------------
a b 1
q r 0.125
s t 0.0925
m w 0.0002
o p 0.375
It's my code;
connection.Open();
//Some insignificant operations here.
for (int q = 0; q < w.Length; q++)
{
for (int a = q + 1; a < w.Length; a++)
{
string query = ".....";
SqlDataAdapter myAdapter = new SqlDataAdapter(query, connection);
DataTable myTable = new DataTable();
myAdapter.Fill(myTable);
//I started here for finding maximum number in DataTable.
int[] myColumn = dt.AsEnumerable().Select(x => x.Field<int>("Column3")).ToArray();
int myMaximum = myColumn.Max();
//I don't know what should I do after that.
foreach (DataRow myRows in myTable.Rows)
{
//Some significant operations again...
dgv1.Rows.Add(...);
}
}
}
connection.Close();
I would suggest get rid of DataTable and work with a plain c# classes.
DataTable is "heavy" structure which do much more than you need and in most of the cases you don't need it.
Create class to represent your data
public class Item
{
public string Column1 { get; set; }
public string Column2 { get; set; }
public int Column3 { get; set; }
}
Load data
public List<Item> LoadData()
{
var query = "SELECT Column1, Column2, Column3 FROM Table";
using (var connection = new SqlConnection(connectionString))
using (var command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = query;
using (var reader = command.ExecuteReader())
{
var data = new List<Item>();
while(reader.Read())
{
var item = new Item
{
Column1 = reader.GetString(0),
Column1 = reader.GetString(1),
Column1 = reader.GetInt32(2)
};
data.Add(item);
}
return data;
}
}
}
Create class which will represent calculated data
public class CalculatedItem
{
public string Column1 { get; }
public string Column2 { get; }
public decimal Calculated { get; }
public CalculatedItem(Item item, decimal maxValue)
{
Column1 = item.Column1;
Column2 = item.Column2;
Calculated = (decimal)item.Column3 / maxValue
}
}
Usage
var data = LoadData();
var maxValue = data.Select(item => item.Column3).Max();
var calculatedData = data.Select(item => new CalculatedItem(item, maxValue)).ToList();
// Bind data to the DataGridView
DataGridView1.DataSource = calculatedDate;
Give this a go:
var max = myTable.Rows.Cast<DataRow>().Max(r => r.Field<double>("Column 3"));
foreach (var row in myTable.Rows.Cast<DataRow>())
{
row["Column 3"] = row.Field<double>("Column 3") / max;
}
Runable test code:
var myTable = new DataTable();
myTable.Columns.Add("Column 3", typeof(double));
myTable.Rows.Add(20_00_000);
myTable.Rows.Add(250_000);
myTable.Rows.Add(185_000);
myTable.Rows.Add(400_000);
myTable.Rows.Add(750_000);
Console.WriteLine(String.Join(", ", myTable.Rows.Cast<DataRow>().Select(r => r.Field<double>("Column 3"))));
var max = myTable.Rows.Cast<DataRow>().Max(r => r.Field<double>("Column 3"));
foreach (var row in myTable.Rows.Cast<DataRow>())
{
row["Column 3"] = row.Field<double>("Column 3") / max;
}
Console.WriteLine(String.Join(", ", myTable.Rows.Cast<DataRow>().Select(r => r.Field<double>("Column 3"))));
This outputs:
2000000, 250000, 185000, 400000, 750000
1, 0.125, 0.0925, 0.2, 0.375
Update based on "Column 3" being an int.
Console.WriteLine(String.Join(", ", myTable.Rows.Cast<DataRow>().Select(r => r.Field<int>("Column 3"))));
int max = myTable.Rows.Cast<DataRow>().Max(r => r.Field<int>("Column 3"));
double[] results = myTable.Rows.Cast<DataRow>().Select(r => (double)r.Field<int>("Column 3") / max).ToArray();
Console.WriteLine(String.Join(", ", results));
Now just index in to results when you build your DataGridView.
Have you considered adding a 4th column to the existing table? Make the column an “Expression” column to do the math as you describe, then hide the third column?
An example of this is below…
DataTable GridTable;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
GridTable = GetDataTable();
FillTable(GridTable);
AddExpressionColumn(GridTable);
dataGridView1.DataSource = GridTable;
dataGridView1.Columns["Column3"].Visible = false;
}
private DataTable GetDataTable() {
DataTable dt = new DataTable();
dt.Columns.Add("Column1", typeof(string));
dt.Columns.Add("Column2", typeof(string));
dt.Columns.Add("Column3", typeof(int));
return dt;
}
private void FillTable(DataTable dt) {
dt.Rows.Add("a", "b", 2000000);
dt.Rows.Add("q", "r", 250000);
dt.Rows.Add("s", "t", 185000);
dt.Rows.Add("m", "w", 400000);
dt.Rows.Add("o", "p", 750000);
}
private void AddExpressionColumn(DataTable dt) {
DataColumn expCol = new DataColumn("Result", typeof(decimal));
dt.Columns.Add(expCol);
string expressionString = "Column3 / " + GetMaxValue(dt).ToString();
expCol.Expression = expressionString;
}
private int GetMaxValue(DataTable dt) {
int max = (int)dt.Compute("Max(Column3)", "");
if (max == 0)
return 1;
return max;
}
I tried this solution below:
This Row already belongs to another table error when trying to add rows?
I have a Datatable that contains 597 Columns and 20 Rows and are trying to export the data to excel. However, Excel has a maximum column count 256 and so I need to divide the source data into 3 datatables to make the export work.
Below is the code I have written.
var dtmasterdata = data.Tables[name];
for (int j = 1; j < datatableNumberCount; j++)
{
DataTable dt2 = new DataTable();
dt2.TableName = "Master_" + j;
dt2 = dtmasterdata.Copy();
foreach (DataColumn col in dtmasterdata.Columns)
{
DataColumn dtcol = new DataColumn();
dtcol = col;
dt2.Columns.Add(dtcol.ColumnName, dtcol.DataType);
}
for (int k = 0; k < dtmasterdata.Rows.Count; k++)
{
DataRow dr = dt2.NewRow();
dr = dtmasterdata.Rows[k];
dt2.ImportRow(dtmasterdata.Rows[k]);
//dt2.Rows.Add(dr.ItemArray);
}
After that I need to delete few columns like below and I want to create 3 datatables
foreach (DataColumn col in dtmasterdata.Columns)
{
if (j == 1)
{
// condition 1
if (col.Ordinal >= 255)
{
dt2.Columns.RemoveAt(col.Ordinal);
}
}
if (j == 2)
{
// condition 2.
if (col.Ordinal < 255 || col.Ordinal >= 510)
{
dt2.Columns.RemoveAt(col.Ordinal);
}
}
if (j == 3)
{
// condition 3.
if (col.Ordinal <= 510 || col.Ordinal >= 765)
{
dt2.Columns.Add(col);
}
}
}
int worksheetNumber = 1;
string worksheetNameWithNumber = "Master Data";
if (worksheetNumber > 1)
worksheetNameWithNumber = String.Format("{0}_{1}", ws1, worksheetNumber.ToString());
Infragistics.Excel.Worksheet worksheet = wb.Worksheets.Add(worksheetNameWithNumber);
Infragistics.WebUI.UltraWebGrid.UltraWebGrid masterData1 = new Infragistics.WebUI.UltraWebGrid.UltraWebGrid("masterDataGrid");
masterData1.Browser = Infragistics.WebUI.UltraWebGrid.BrowserLevel.UpLevel;
masterData1.DataSource = dt2;
masterData1.DataMember = "Master_" + j;
masterData1.DisplayLayout.HeaderStyleDefault.Font.Bold = true;
masterData1.DisplayLayout.HeaderStyleDefault.Font.Name = "Arial";
masterData1.DisplayLayout.HeaderStyleDefault.Font.Size = FontUnit.Parse("10px");
masterData1.DisplayLayout.HeaderStyleDefault.BackColor = System.Drawing.Color.LightGray;
masterData1.DisplayLayout.RowStyleDefault.Font.Name = "Arial";
masterData1.DisplayLayout.RowStyleDefault.Font.Size = FontUnit.Parse("10px");
Infragistics.WebUI.UltraWebGrid.UltraGridBand masterBand1 = new Infragistics.WebUI.UltraWebGrid.UltraGridBand();
masterData1.Bands.Add(masterBand1);
dgResults.Controls.Add(masterData1);
masterData1.DataBind();
wb.ActiveWorksheet = worksheet;
this.ugWebGridExporter.Export(masterData1, worksheet);
worksheetNumber++;
Your error is because you are trying to add a column to a datatable that already belongs to your source datatable.
dt2.Columns.Add(col);
You can't just iterate through the columns of a datatable and add them to another.
I've a solution to this, which involves cloning the source data and removing what you don't need.
1st, make 3 clones of the datatables you need. Below is an example with me creating my own source table with 596 columns. Notice that clone only takes the data table structure, no data!
var source597ColsTable = new DataTable("Source");
for (var i = 0; i <= 596; i++)
{
source597ColsTable.Columns.Add(new DataColumn("Column" + i , typeof(string)));
}
DataRow newRow = source597ColsTable.NewRow();
source597ColsTable.Rows.Add(newRow);
var cols0To199Table = source597ColsTable.Clone();
var cols200To399Table = source597ColsTable.Clone();
var cols400To596Table = source597ColsTable.Clone();
Next copy all the rows from the source table into the clones. The below is a simple function to do so.
private DataTable CopyRowsFromSource(DataTable sourceTable, DataTable destinationTable)
{
foreach (DataRow row in sourceTable.Rows)
{
destinationTable.Rows.Add(row.ItemArray);
}
return destinationTable;
}
Then call this function for each of your tables.
cols0To199Table = CopyRowsFromSource(source597ColsTable, cols0To199Table);
cols200To399Table = CopyRowsFromSource(source597ColsTable, cols200To399Table);
cols400To596Table = CopyRowsFromSource(source597ColsTable, cols400To596Table);
Finally, remove all the columns from the datatables to give you your split.
private DataTable RemoveColumns(DataTable table, int startCol, int endCol)
{
var colsToRemove = new List<DataColumn>();
for (var colCount = startCol; colCount <= endCol; colCount++)
{
colsToRemove.Add(table.Columns[colCount]);
}
foreach (DataColumn col in colsToRemove)
{
table.Columns.Remove(col);
}
return table;
}
Then call.. again for each cloned table.
cols0To199Table = RemoveColumns(cols0To199Table, 200, 596);
cols200To399Table = RemoveColumns(cols200To399Table, 0, 199);
cols200To399Table = RemoveColumns(cols200To399Table, 200, 396);
cols400To596Table = RemoveColumns(cols400To596Table, 0, 399);
After running this, you will have 3 datatables, columns 0-199, 200-399 and 400-596.
Hope that helps.
I am not sure to have really understood all of your code, but to copy a subset of columns to another datatable there is a very simple method in the DataView class named ToTable where you can list the columns you want in the new table. As added bonus, this method copies also the data in the 20 rows of your original table.
So the only difficult is to list these columns to the method.
You can proceed in this way using linq over the DataColumn collection
string[] firstCols = dtmasterdata.Columns
.Cast<DataColumn>()
.Take(255)
.Select(x => x.ColumnName).ToArray();
string[] secondCols = dtmasterdata.Columns
.Cast<DataColumn>()
.Skip(255)
.Take(255)
.Select(x => x.ColumnName).ToArray();
string[] thirdCols = dtmasterdata.Columns
.Cast<DataColumn>()
.Skip(510)
.Select(x => x.ColumnName).ToArray();
DataTable t1 = dtmasterdata.DefaultView.ToTable("Master_1", false, firstCols);
DataTable t2 = dtmasterdata.DefaultView.ToTable("Master_2", false, secondCols);
DataTable t3 = dtmasterdata.DefaultView.ToTable("Master_3", false, thirdCols);
I am using SQLite to store the data for my application.
The application has a UI that loads the data from the SQLite database to display it table by table to the user. Basically the user can click left or right and view the other tables one by one.
The user can also click a button that will show a print preview of the data and let them print it. I have this working, but I am having some issues devising a dynamic way to display ANY table on the print preview screen perfectly. My concerns are if some column titles are too long etc, basically how to calculate the size of each column etc. Should I loop through and find the max character size of any text in the entire column and then set the column width to just wider than that or is there an easier way to do this?
I also write the data-table to a comma separated csv file so it might be a better alternative to use an existing solution to print from a csv file if any of you know such a solution then please suggest it!
Anyway here is the currently existing code:
// ------------------------ code that gets called when the print button is clicked ----------------------------
// holds the row data
List<string[]> myList = new List<string[]>();
if(ReportPage == 1)
{
int rowCount = MyTestTable.RowCount;
for(int i = 0; i <rowCount; i++)
{
MyTestTable.SelectedRowIndex = i;
var row1 = MyTestTable.GetSelectedRow();
var cols1 = row1.ItemArray;
string Col1 = cols1[row1.FindIndexOfColumn("Col1")].ToString();
string Col2 = cols1[row1.FindIndexOfColumn("Col2")].ToString();
string Col3 = cols1[row1.FindIndexOfColumn("Col3")].ToString();
string Col4 = cols1[row1.FindIndexOfColumn("Col4")].ToString();
string Col5 = cols1[row1.FindIndexOfColumn("Col5")].ToString();
string Col6 = cols1[row1.FindIndexOfColumn("Col6")].ToString();
string Col7 = cols1[row1.FindIndexOfColumn("Col7")].ToString();
myList.Add(new string[] { Col1, Col2, Col3, Col4, Col5, Col6, Col7 });
}
string[] cols = new string[7];
cols[0] = "Col1";
cols[1] = "Col2";
cols[2] = "Col3";
cols[3] = "Col4";
cols[4] = "Col5";
cols[5] = "Col6";
cols[6] = "Col7";
PrintUtility.SetUpDocument("TEST", cols, myList);
}
PrintUtility.TestNewReport();
// ---------------------- plugin code that gets called from the application
namespace PrintUtility
{
public class PrintUtility : UserComponentBase
{
public PrintDocument document;
public DataGridView dataGridView;
public PrintUtility()
{
document = new PrintDocument();
dataGridView = new DataGridView();
}
public void SetUpDocument(string title, string[] cols, List<string[]> rows)
{
document = new PrintDocument();
dataGridView = new DataGridView();
document.DocumentName = title;
document.DefaultPageSettings.Landscape = true;
document.PrintPage += PrintPage;
DataGridView dataGrid = new DataGridView();
dataGrid.ColumnCount = cols.Length;
for (int i = 0; i < cols.Length; i++ )
{
dataGrid.Columns[i].Name = cols[i];
}
foreach(string[] row in rows)
{
dataGrid.Rows.Add(row);
}
this.dataGridView = dataGrid;
document.DocumentName = title;
document.PrintPage += PrintPage;
}
public PrintDocument GetDocument()
{
return this.document;
}
private DataTable ConvertListToDataTable(List<string[]> list)
{
// New table.
DataTable table = new DataTable();
// Get max columns.
int columns = 0;
foreach (var array in list)
{
if (array.Length > columns)
{
columns = array.Length;
}
}
// Add columns.
for (int i = 0; i < columns; i++)
{
table.Columns.Add();
}
// Add rows.
foreach (var array in list)
{
table.Rows.Add(array);
}
return table;
}
public void TestNewReport()
{
Report report = new Report(new PdfFormatter());
FontDef fd = new FontDef(report, "Helvetica");
FontProp fp = new FontPropMM(fd, 4);
FontProp fp_Title = new FontPropMM(fd, 6);
FontProp fp_Word = new FontPropMM(fd, 3);
fp_Title.bBold = true;
List<string> columns = new List<string>();
foreach (DataGridViewColumn column in dataGridView.Columns)
{
columns.Add(column.Name.ToString());
}
List<List<string>> rows = new List<List<string>>();
foreach (DataGridViewRow row in dataGridView.Rows)
{
List<string> rowSingle = new List<string>();
foreach (DataGridViewCell cell in row.Cells)
{
try
{
rowSingle.Add(cell.Value.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
rows.Add(rowSingle);
}
// AUDIT TABLE ( This way of doing things is not dynamic )
Page page = new Page(report);
page.SetLandscape();
int x = 10;
int y = 40;
int pastLength = 0;
foreach(string col in columns)
{
x += ((pastLength * 14) + 31);
page.Add(x, y, new RepString(fp_Title, col));
pastLength = col.Length;
}
page.Add(0, 52, new RepString(fp_Title, "_________________________________________________________________"));
/* Dynamic way starting
int rowX = 10;
int rowY = 105;
foreach (List<string> row in rows)
{
int pastLength2 = 0;
foreach (string rowItem in row)
{
page.Add(rowX, rowY, new RepString(fp_Word, rowItem));
rowX += ((pastLength * 14) + 31);
pastLength2 = rowItem.Length;
}
rowY += 30;
}
*/
fp_Title.rSizeMM = 8;
int amountRowsPerPageSoFar = 0;
int rowY = 80;
foreach (List<string> row in rows)
{
try
{
string iDItem = row[0];
page.Add(40, rowY, new RepString(fp_Word, iDItem));
string typeItem = row[1];
page.Add(120, rowY, new RepString(fp_Word, typeItem));
string descriptionItem = row[2];
page.Add(190, rowY, new RepString(fp_Word, descriptionItem));
string timestampItem = row[3];
page.Add(375, rowY, new RepString(fp_Word, timestampItem));
string userItem = row[4];
page.Add(555, rowY, new RepString(fp_Word, userItem));
string stationItem = row[5];
page.Add(655, rowY, new RepString(fp_Word, stationItem));
string activeItem = row[6];
page.Add(775, rowY, new RepString(fp_Word, activeItem));
amountRowsPerPageSoFar++;
rowY += 30;
if (amountRowsPerPageSoFar >= 17)
{
page = new Page(report);
page.SetLandscape();
amountRowsPerPageSoFar = 0;
rowY = 40;
}
}
catch (Exception ex)
{
}
}
RT.ViewPDF(report, "TestReport.pdf");
}
}
}
I am using C#. I have two data tables and i want to find the rows of first data table into second data table.
Example.
First data table's data:
1 inam
2 sohan
Second data tables's data:
3 ranjan
1 inam
2 sohan
Now i want to know the index of first two rows of first data table into second data table.
Please help guys.
Any answer or advice
Best Regards
You can use following extension method which returns the first index of a "sub-sequence":
// I've used String.Join to get something that is comparable easily
// from the ItemArray that is the object-array of all fields
IEnumerable<string> first = table1.AsEnumerable()
.Select(r => string.Join(",",r.ItemArray)); //
IEnumerable<string> second = table2.AsEnumerable()
.Select(r => string.Join(",", r.ItemArray));
int index = second.IndexOfSequence(first, null); // 1
Here the extension:
public static int IndexOfSequence<TSource>(this IEnumerable<TSource> input, IEnumerable<TSource> sequence, IEqualityComparer<TSource> comparer)
{
if (input == null) throw new ArgumentNullException("input");
if (sequence == null) throw new ArgumentNullException("sequence");
if (!sequence.Any()) throw new ArgumentException("Sequence must not be empty", "sequence");
if (comparer == null)
{
comparer = EqualityComparer<TSource>.Default;
}
int index = -1;
int firstIndex = -1;
bool found = false;
TSource first = sequence.First();
using (IEnumerator<TSource> enumerator = input.GetEnumerator())
{
using (IEnumerator<TSource> enumerator2 = sequence.GetEnumerator())
{
enumerator2.MoveNext();
while (enumerator.MoveNext())
{
index++;
found = comparer.Equals(enumerator.Current, enumerator2.Current);
if (found && firstIndex == -1) firstIndex = index;
if (found && !enumerator2.MoveNext())
return firstIndex;
}
}
}
return -1;
}
tested with this sample data:
var table1 = new DataTable();
table1.Columns.Add("ID", typeof(int));
table1.Columns.Add("Name");
var table2 = table1.Clone();
table1.Rows.Add(1, "inam");
table1.Rows.Add(2, "Sohan");
table2.Rows.Add(3, "ranjan");
table2.Rows.Add(1, "inam");
table2.Rows.Add(2, "Sohan");
If you don't have much volume this might work....
var tableOneIndex = -1;
var tableTwoIndex = -1;
foreach (var tableOneRow in tableOne.Rows)
{
tableOneIndex++;
foreach (var tableTwoRow in tableTwo.Rows)
{
tableTwoIndex++;
if (tableOneRow["name"].ToString() == tableTwoRow["name"].ToString())
{
// Do whatever you wanted to do with the index values
}
}
}
As a simple solution, this should suffice:
// Create and populate data tables
DataTable dataTable1 = new DataTable();
dataTable1.Columns.Add("Name", typeof(string));
DataRow row1 = dataTable1.NewRow();
row1["Name"] = "Inam";
DataRow row2 = dataTable1.NewRow();
row2["Name"] = "Sohan";
dataTable1.Rows.Add(row1);
dataTable1.Rows.Add(row2);
DataTable dataTable2 = new DataTable();
dataTable2.Columns.Add("Name", typeof(string));
DataRow row3 = dataTable2.NewRow();
row3["Name"] = "Ranjan";
DataRow row4 = dataTable2.NewRow();
row4["Name"] = "Inam";
DataRow row5 = dataTable2.NewRow();
row5["Name"] = "Sohan";
dataTable2.Rows.Add(row3);
dataTable2.Rows.Add(row4);
dataTable2.Rows.Add(row5);
// Loop through rows in first table
foreach (DataRow row in dataTable1.Rows)
{
int rowIndexInSecondTable = 0;
// Loop through rows in second table
for (int i = 0; i < dataTable2.Rows.Count; i++)
{
// Check if the column values are the same
if (row["Name"] == dataTable2.Rows[i]["Name"])
{
// Set the current index and break to stop further processing
rowIndexInSecondTable = i;
break;
}
}
// The index of the row in the second table is now stored in the rowIndexInSecondTable variable, use it as needed, for example, writing to the console
Console.WriteLine("Row with name '" + row["Name"] + "' found at index " + rowIndexInSecondTable.ToString());
}
How can I remove EVERY duplicating row in a DataTable, based on the value of two columns that are in duplication. Unfortunately, I am unable to find the equivalent LINQ Query. (I dont want distinct values even). The table below shall explain my problem
I want to delete every row in duplication based on Column_A and Column_B
COLUMN_A COLUMN_B COLUMN_C COLUMN_D.....
A B
C D
E F
G H
A B
E F
EXPECTED OUTPUT:
COLUMN_A COLUMN_B COLUMN_C COLUMN_D.....
C D
G H
Please help
var rowsToDelete = dataTable.AsEnumerable()
.GroupBy(r => new{A=r["COLUMN_A"],B=r["COLUMN_B"]})
.Where(g => g.Count() > 1)
.SelectMany(g=>g)
.ToList();
foreach (var row in rowsToDelete)
{
dataTable.Rows.Remove(row);
}
You can try with this sample
Link : http://geekswithblogs.net/ajohns/archive/2004/06/24/7191.aspx
Coding
private static void RemoveDuplicates(DataTable tbl,
DataColumn[] keyColumns)
{
int rowNdx = 0;
while(rowNdx < tbl.Rows.Count-1)
{
DataRow[] dups = FindDups(tbl, rowNdx, keyColumns);
if(dups.Length>0)
{
foreach(DataRow dup in dups)
{
tbl.Rows.Remove(dup);
}
}
else
{
rowNdx++;
}
}
}
private static DataRow[] FindDups(DataTable tbl,
int sourceNdx,
DataColumn[] keyColumns)
{
ArrayList retVal = new ArrayList();
DataRow sourceRow = tbl.Rows[sourceNdx];
for(int i=sourceNdx + 1; i<tbl.Rows.Count; i++)
{
DataRow targetRow = tbl.Rows[i];
if(IsDup(sourceRow, targetRow, keyColumns))
{
retVal.Add(targetRow);
}
}
return (DataRow[]) retVal.ToArray(typeof(DataRow));
}
private static bool IsDup(DataRow sourceRow,
DataRow targetRow,
DataColumn[] keyColumns)
{
bool retVal = true;
foreach(DataColumn column in keyColumns)
{
retVal = retVal && sourceRow[column].Equals(targetRow[column]);
if(!retVal) break;
}
return retVal;
}
Test
// create an example datatable with duplicate rows
DataTable tbl = new DataTable();
tbl.Columns.Add("ColumnA");
tbl.Columns.Add("ColumnB");
tbl.Columns.Add("ColumnC");
for(int i = 0; i<10; i++)
{
DataRow nr = tbl.NewRow();
nr["ColumnA"] = "A" + i.ToString();
nr["ColumnB"] = "B" + i.ToString();
nr["ColumnC"] = "C" + i.ToString();
tbl.Rows.Add(nr);
// duplicate
nr = tbl.NewRow();
nr["ColumnA"] = "A" + i.ToString();
nr["ColumnB"] = "B" + i.ToString();
nr["ColumnC"] = "C" + i.ToString();
tbl.Rows.Add(nr);
}
PrintRows(tbl); // show table with duplicates
//Create an array of DataColumns to compare
//If these columns all match we consider the
//rows duplicate.
DataColumn[] keyColumns =
new DataColumn[]{tbl.Columns["ColumnA"],
tbl.Columns["ColumnA"]};
//remove the duplicates
RemoveDuplicates(tbl, keyColumns);