Merge datatables but ignore duplicated rows - c#

I have the following code, its a custom people picker for sharepoint 2010.
It searches by username, but also by the person name.
Because its a contains search, if I try with part of my username: cia
It shows my duplicated rows because that matches the username but also the person name.
this is my code (I cant use LINQ:
protected override int IssueQuery(string search, string groupName, int pageIndex, int pageSize)
{
try
{
// Find any user that has a matching name
var table = ADHelper.ExecuteNameQuery(RootPath, search);
// 20249: Search by username, method was already done, but it was not being called.
var table2 = ADHelper.ExecutesAMAccountNameQuery(search);
table2.Merge(table,);
PickerDialog.Results = table2;

Normally the DataTable.Merge method removes duplicates implicitely. But only when all columns' values are the same.
I'm not sure if there is something simplier(you've mentioned that you cannot use LINQ), but you could merge both and remove the duplicates afterwards:
List<string> dupColumns = new List<string>();
dupColumns.Add("ColumnA");
dupColumns.Add("ColumnB");
table2.Merge(table,);
RemoveDuplicates(table2, dupColumns);
And here the remove-duplicates function:
private void RemoveDuplicates(DataTable table, List<string> keyColumns)
{
Dictionary<string, string> uniquenessDict = new Dictionary<string, string>(table.Rows.Count);
System.Text.StringBuilder sb = null;
int rowIndex = 0;
DataRow row;
DataRowCollection rows = table.Rows;
while (rowIndex < rows.Count)
{
row = rows[rowIndex];
sb = new System.Text.StringBuilder();
foreach (string colname in keyColumns)
{
sb.Append(((string)row[colname]));
}
if (uniquenessDict.ContainsKey(sb.ToString()))
{
rows.Remove(row);
}
else
{
uniquenessDict.Add(sb.ToString(), string.Empty);
rowIndex++;
}
}
}

you should the .ToTable function
here is a sample code
DataTable DT1 = new DataTable();
DT1.Columns.Add("c_" + DT1.Columns.Count);
DT1.Columns.Add("c_" + DT1.Columns.Count);
DT1.Columns.Add("c_" + DT1.Columns.Count);
DataRow DR = DT1.NewRow();
DR[0] = 0;
DR[1] = 1;
DR[2] = 2;
DT1.Rows.Add(DR);
DataTable DT2 = new DataTable();
DT2.Columns.Add("c_" + DT2.Columns.Count);
DT2.Columns.Add("c_" + DT2.Columns.Count);
DT2.Columns.Add("c_" + DT2.Columns.Count);
DT2.Columns.Add("c_" + DT2.Columns.Count);
DR = DT2.NewRow();
DR[0] = 0;
DR[1] = 1;
DR[2] = 2;
DR[3] = 3;
DT2.Rows.Add(DR);
DT1.Merge(DT2);
Trace.IsEnabled = true;
DataTable DT_3=DT1.DefaultView.ToTable(true,new string[]{"c_1","c_2","c_0"});
foreach (DataRow CDR in DT_3.Rows)
{
Trace.Warn("val",CDR[1]+"");//you will find only one data row
}

Related

Use Button to Go to Next Available ID in Dataview C#

I have the data below, and I used RowFilter to filter the ID, and only show rows that have ID = 111. However, I need to create a "Next" button to be able to go to the next unique ID which is 222. The IDs are not incremental.
Original Table:
Any tips on how to approach this? I am running out of options
string[] columnnames = file.ReadLine().Split('|');
DataTable dt = new DataTable();
foreach (string c in columnnames)
{
dt.Columns.Add(c);
}
string newline;
while ((newline = file.ReadLine()) != null)
{
DataRow dr = dt.NewRow();
string[] values = newline.Split('|');
for (int i = 0; i < values.Length; i++)
{
dr[i] = values[i];
}
dt.Rows.Add(dr);
}
DataView dv = new DataView(dt);
dv.RowFilter = "ID = '111'";
dataGridView1.DataSource = dv;
I used dv.RowFilter = "ID = '111'"; however how can I make it dynamic so it can go to the next ID= 222?
Thanks

CSV reading data with comma's in data

I've got a project that has to do a bulk import to SQL from a CSV file. Creating the data columns has been a success, however, I'm running into a problem with the rows. A comma is used as the delimiter to separate the columns which work great in the column names, but not in the rows of data. Some data has a comma to split name and surnames. Together with this, only every second field(column) is enclosed in double quotes. Using all this is breaking the rows in many more columns than it should. I have suggested changing the delimiter to a semicolon which actually works great and everything works fine, except this is not accepted by the customer as they don't want to change anything.
This is what I've done:
private static DataTable ImportFordEmailList(string csvFilePath)
{
DataTable csvData = new DataTable();
DataTable dt = new DataTable();
dt.Columns.Add("ColumnName");
dt.Rows.Clear();
try
{
using (TextFieldParser csvReader = new TextFieldParser(csvFilePath))
{
// csvReader.TextFieldType = FieldType.Delimited;
csvReader.SetDelimiters(new string[] { "," });
csvReader.HasFieldsEnclosedInQuotes = false;
csvReader.TrimWhiteSpace = true;
string[] colFields = csvReader.ReadFields();
foreach (string column in colFields)
{
if (dt.Rows.Count > 0)
{
string newColumn = Regex.Replace(column, "[^A-Za-z0-9]", "");
string findColum = "ColumnName = '" + newColumn.Trim() + "'";
DataRow[] foundRows = dt.Select(findColum);
if (foundRows.Length == 0)
{
DataRow dr = dt.NewRow();
dr["ColumnName"] = newColumn.Trim();
dt.Rows.Add(dr);
}
else
{
DataRow dr = dt.NewRow();
dr["ColumnName"] = newColumn.Trim() + "1";
dt.Rows.Add(dr);
}
}
else
{
string newColumn = column.Replace("'", "");
newColumn = newColumn.Replace(" ", "");
string clean = Regex.Replace(newColumn, "[^A-Za-z0-9 ]", "");
DataRow dr = dt.NewRow();
dr["ColumnName"] = clean.Trim();
dt.Rows.Add(dr);
}
}
foreach (DataRow row in dt.Rows)
{
string colName = Regex.Replace(row["ColumnName"].ToString().Trim(), "/^[ A-Za-z0-9]*$/", "");
DataColumn datecolumn = new DataColumn(colName);
datecolumn.AllowDBNull = true;
csvData.Columns.Add(datecolumn);
}
while (!csvReader.EndOfData)
{
string[] fieldData = csvReader.ReadFields();
for (int i = 0; i < fieldData.Length; i++)
{
if (fieldData[i] == "")
{
fieldData[i] = null;
}
}
foreach (string s in fieldData)
{
s.Replace("\"","");
Regex.Replace(s, "/^[ A-Za-z0-9 '#.()]", "");
string a = s;
}
csvData.Rows.Add(fieldData);
}
}
}
catch (Exception ex)
{
}
return csvData;
}
This is an example of how the data looks like:
Is there a way that I can work around this and make this work?
----- EDIT, Add data sample as text --------
Name,Name,Email,Manager Level1,Level 1 manager's email,Manager Level2,Level 2 manager's email
Adams, D. (Deon) ,"Adams, Deon. (D) ",username#email.com,"Masete, Thabo (B.T.)",username#email.com,"Fraser, Mervyn (M.)",username#email.com
Akaramunkongwanit, S. (Sirapra) ,"Akaramunkongwanit, Sirapra (S.)",username#email.com> ,"Naraphirom, Suphajitphat (Pin.)",username#email.com,"Jeeradeepalung, Jirawat (Jee.)",username#email.com
Angel, L. (Dave) ,"Angel, Dave (L.) ",username#email.com,"Causton, Keith (K.H.) ",username#email.com,"White, Chris- Manf Eng (C.F.) ",username#email.com
Apairat, J. (Janjira),"Apairat, Janjira (J.) "username#email.com,"Choksiriwanna, Phatthar (Patsy.)",username#email.com,"Phusitpoykai, Rachawan (R.) ",username#email.com

Populating rows with data from a stored procedure using DataTables

I want to populate some fields on a PDF with data from a SQL database, I have a data access class that works but I can't get the values from the rows:
DataAccess DA = new DataAccess();
DataSet S = DA.CashData();
DataTable Cash = S.Tables[0];
DataTable Other = S.Tables[1];
DataTable Check = S.Tables[2];
DataTable Else = S.Tables[3];
string s = "";
foreach (DataRow row in Cash.Rows)
{
foreach (DataColumn col in Cash.Columns)
{
for (int i = 0; i < Cash.Columns.Count; i++)
{
DataRow cashRow = Cash.Rows[i];
s = s + cashRow[0].ToString() + ";";
}
}
Console.ReadLine();
}
string[] data = s.Split(';');
cell32.AddElement(new Paragraph(""));
cell32.AddElement(new Paragraph(data[0]));
cell32.AddElement(new Paragraph(data[1]));
The columns show data but the rows do not
If I understand your source correctly, you have a wrong execution in your loop for the rows, please consider this edited source:
DataAccess DA = new DataAccess();
DataSet S = DA.CashData();
DataTable Cash = S.Tables[0];
DataTable Other = S.Tables[1];
DataTable Check = S.Tables[2];
DataTable Else = S.Tables[3];
string s = "";
foreach (DataRow row in Cash.Rows)
{
for (int i = 0; i < row.Columns.Count; i++)
{
s += row[0].ToString() + ";";
}
Console.ReadLine();
}
string[] data = s.Split(';');
cell32.AddElement(new Paragraph(""));
cell32.AddElement(new Paragraph(data[0]));
cell32.AddElement(new Paragraph(data[1]));

from datatable to array، no loop

Following Code for transmission to the list box is :
DataTable dt = new DataTable();
DataColumn dc = new DataColumn("BestSite", typeof(string));
dt.Columns.Add(dc);
for (int i = 1; i <= 10; i++)
{
DataRow dr = dt.NewRow();
dr[0] = i.ToString() + " = stackoverflow";
dt.Rows.Add(dr);
}//EndFor
var Query = from mycolumn in dt.AsEnumerable()
where mycolumn.Field<string>("BestSite") != string.Empty
select mycolumn;
listBox1.DataSource = Query.AsDataView();
listBox1.DisplayMember = "BestSite";
Transfer to array what should be? no loop
string[] myvalue = new string[Query.AsDataView().Count];
Finally realized
Correct answer :
private string ConvertToString(DataRow dr)
{
return Convert.ToString(dr[0]);
}
private void button1_Click(object sender, EventArgs e)
{
DataTable dt = new DataTable();
DataColumn dc = new DataColumn("BestSite", typeof(string));
dt.Columns.Add(dc);
for (int i = 1; i <= 10; i++)
{
DataRow dr = dt.NewRow();
dr[0] = i.ToString() + " = stackoverflow";
dt.Rows.Add(dr);
}//EndFor
//var Query = from mycolumn in dt.AsEnumerable()
// where mycolumn.Field<string>("BestSite") != string.Empty
// select mycolumn;
DataRow[] myrow = new DataRow[dt.Rows.Count];
dt.Rows.CopyTo(myrow, 0);
string[] myString = Array.ConvertAll(myrow, new Converter<DataRow, string>(ConvertToString));
foreach (string a in myString)
{
listBox1.Items.Add(a);
}
}
If I understood your question correctly...
string[] myvalue = Query.Select(i => i.Field<string>("BestSite")).ToArray();
This example assumes:
=> datatable is non zero length
=> values are "parsable" to double.
coln = "PurchasePrice";
double[] arr_val = Array.ConvertAll<DataRow, double>
(
dattbl.Select(),
delegate (DataRow rw) { return
double.Parse(rw[coln].ToString()); }
);
// ... and for example
double total = arr_val.Sum();

How to 'union' 2 or more DataTables in C#?

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

Categories