compare two datatables with multiple columns with a unique id - c#

I have written a program to compare two datatables with unique ids and create another datatable to insert certain columns which have the same id in common. I have demonstrated my requirement below.
These are the tables that needs to be compared:
And i need the output as below
but i receive an empty table as the result. I cannot understand where have i gone wrong. Could you please help me on this. I have provided my coding below.Please not that quantity and input are two datatables
DataTable result = new DataTable();
result.Columns.AddRange(new DataColumn[2] { new DataColumn("id"), new DataColumn("qty") });
foreach (DataRow row1 in input.Rows)
{
foreach (DataRow row2 in quantity.Rows)
{
if (row1["id"].ToString() == row2["id"].ToString())
{
result.ImportRow(row2);
}
else
{
result.ImportRow(row1);
}
}
}
return result;

You need a Left Join of 2 Data tables.
DataTable dtinput = new DataTable();
DataTable dtquantity = new DataTable();
dtinput.Columns.Add("id",typeof(int));
dtinput.Rows.Add("2");
dtinput.Rows.Add("4");
dtinput.Rows.Add("7");
dtquantity.Columns.Add("id", typeof(int));
dtquantity.Columns.Add("qty", typeof(int));
dtquantity.Rows.Add("1", "12");
dtquantity.Rows.Add("2", "13");
dtquantity.Rows.Add("3", "5");
dtquantity.Rows.Add("4", "6");
dtquantity.Rows.Add("7", null);
var results = from table1 in dtinput.AsEnumerable()
join table2 in dtquantity.AsEnumerable()
on (int)table1["id"] equals (int)table2["id"]
into outer
from row in outer.DefaultIfEmpty<DataRow>()
select row;
DataTable dt = results.CopyToDataTable();
This diagram should help you in future:

Try something like this:
var result = input.Rows.Where(x => quantity.Rows.Amy(y => x == y));
I hope this helps!

Related

What is the return type of a linq query result from two tables?

I want to return a list of extracted data from two tables into a DataGridView with given column names.
public DataTable GetInfo(int id)
{
var stats = new List<string>();
using MyContext db = new MyContext();
var linqResult = (from Round in db.Rounds
join Hands in db.Hands on Round.RoundID equals Hands.RoundID
where Round.UserID == id
select new
{
ID = Round.RoundID,
Status = Round.Status,
DatePlayed = Round.DatePlayed,
Cards = Hands.PlayerCards,
Score = Hands.PlayerScore
});
return (DataTable)linqResult;
}
The code above returns the correct values but the names of the columns are set to default in the DataGridView. I did some google searching and tried ToString() but I am not able to figure out the correct return type as linqResult is defined as a Generic List with anonymous types. What I want is to avoid naming all the columns manually.
Thank you!
Code below will put your data into a DataTable
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(string));
dt.Columns.Add("Status", typeof(string));
dt.Columns.Add("DatePlayed", typeof(DateTime));
dt.Columns.Add("Cards", typeof(string));
dt.Columns.Add("Score", typeof(int));
foreach (var result in linqResult)
{
dt.Rows.Add(new object[] { result.ID, result.Status, result.DatePlayed, result.Cards, result.Score });
}

Delete using LINQ by joining two datatables

I have two datatable DurationByCurrency(inside a dataset) and Fund which looks like below
I want to delete the rows in Duration By Currency Datatable whose FundCode has value as 2 in Fund Dt by performing a join.
var result = from table1 in raptorDS.Tables[RaptorTable.DurationByCurrency].AsEnumerable()
join table2 in fundDT.AsEnumerable()
on table1.Field<string>("FundCode") equals table2.Field<string>("FundCode") into ps
from row in ps.DefaultIfEmpty()
{
//delete query
}
Please help me on this as I am new to LINQ.
var result = from row1 in raptorDS.Tables[RaptorTable.DurationByCurrency].AsEnumerable()
join row2 in fundDT.AsEnumerable()
on row1.Field<string>("FundCode") equals row2.Field<string>("FundCode")
where row1.Field<string>("value")
equals "2" select row1;
result.ToList().ForEach(row => row.Delete());
sample test code for linqpad:
void Main()
{
//sample data for test
DataSet ds = new DataSet();
ds.Tables.Add(GetTable1());
ds.Tables.Add(GetTable2());
var result = ( from rec1 in ds.Tables[0].AsEnumerable()
join rec2 in ds.Tables[1].AsEnumerable()
on rec1.Field<string>("FC") equals rec2.Field<string>("FC")
where rec2.Field<int>("Value") == 2 select rec1);
result.ToList().ForEach(row => row.Delete());
//now you have only "ABCD" and "AZY" in table 1
//ds.Tables[0].Dump(); linqpad display result
}
DataTable GetTable1()
{
DataTable table = new DataTable();
table.Columns.Add("FC", typeof(string));
table.Rows.Add("ABCD");
table.Rows.Add("XYZ");
table.Rows.Add("AZY");
return table;
}
DataTable GetTable2()
{
DataTable table = new DataTable();
table.Columns.Add("FC", typeof(string));
table.Columns.Add("Value", typeof(int));
table.Rows.Add("ABCD", 1);
table.Rows.Add("XYZ", 2);
table.Rows.Add("AZY",3);
return table;
}

how to add two datatables dt1=2 column ,dt2=3 column, into one datatable dt3=5 column

how to add two datatables?
(dt1=name,phone_number)
and
(dt2=address,cityname)
Result:
(dt3=name,phone_number,address,cityname)
Ruben, you will need some primary key in order to combine both tables unless you are 100% sure that both data tables have the same number of rows and they will always be stored in the correct order.
There is a similar question answered and the link is: Combining n DataTables into a Single DataTable
Here goes the content of the provided link:
"The table has repeating primary keys after the Merge because no primary-key was defined. So either specify the PK or try this method here which i've written from scratch(so it's not really tested):
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)
{
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;
}
You can call it in this way:
var tables = new[] { tblA, tblB, tblC };
DataTable TblUnion = tables.MergeAll("c1");
Used this sample data:
var tblA = new DataTable();
tblA.Columns.Add("c1", typeof(int));
tblA.Columns.Add("c2", typeof(int));
tblA.Columns.Add("c3", typeof(string));
tblA.Columns.Add("c4", typeof(char));
var tblB = new DataTable();
tblB.Columns.Add("c1", typeof(int));
tblB.Columns.Add("c5", typeof(int));
tblB.Columns.Add("c6", typeof(string));
tblB.Columns.Add("c7", typeof(char));
var tblC = new DataTable();
tblC.Columns.Add("c1", typeof(int));
tblC.Columns.Add("c8", typeof(int));
tblC.Columns.Add("c9", typeof(string));
tblC.Columns.Add("c10", typeof(char));
tblA.Rows.Add(1, 8500, "abc", 'A');
tblA.Rows.Add(2, 950, "cde", 'B');
tblA.Rows.Add(3, 150, "efg", 'C');
tblA.Rows.Add(4, 850, "ghi", 'D');
tblA.Rows.Add(5, 50, "ijk", 'E');
tblB.Rows.Add(1, 7500, "klm", 'F');
tblB.Rows.Add(2, 900, "mno", 'G');
tblB.Rows.Add(3, 150, "opq", 'H');
tblB.Rows.Add(4, 850, "qrs", 'I');
tblB.Rows.Add(5, 50, "stu", 'J');
tblC.Rows.Add(1, 7500, "uvw", 'K');
tblC.Rows.Add(2, 900, "wxy", 'L');
tblC.Rows.Add(3, 150, "yza", 'M');
tblC.Rows.Add(4, 850, "ABC", 'N');
tblC.Rows.Add(5, 50, "CDE", 'O');
After DataTable.Merge in MergeAll:
After some modifications to join the rows in MergeAll:
Update
Since this question arose in one of the comments, if the only relation between two tables is the index of a DataRow in the table and you want to merge both tables according to the index:
public static DataTable MergeTablesByIndex(DataTable t1, DataTable t2)
{
if (t1 == null || t2 == null) throw new ArgumentNullException("t1 or t2", "Both tables must not be null");
DataTable t3 = t1.Clone(); // first add columns from table1
foreach (DataColumn col in t2.Columns)
{
string newColumnName = col.ColumnName;
int colNum = 1;
while (t3.Columns.Contains(newColumnName))
{
newColumnName = string.Format("{0}_{1}", col.ColumnName, ++colNum);
}
t3.Columns.Add(newColumnName, col.DataType);
}
var mergedRows = t1.AsEnumerable().Zip(t2.AsEnumerable(),
(r1, r2) => r1.ItemArray.Concat(r2.ItemArray).ToArray());
foreach (object[] rowFields in mergedRows)
t3.Rows.Add(rowFields);
return t3;
}
Sample:
var dt1 = new DataTable();
dt1.Columns.Add("ID", typeof(int));
dt1.Columns.Add("Name", typeof(string));
dt1.Rows.Add(1, "Jon");
var dt2 = new DataTable();
dt2.Columns.Add("Country", typeof(string));
dt2.Rows.Add("US");
var dtMerged = MergeTablesByIndex(dt1, dt2);
The result table contains three columns ID,Name,Country and a single row: 1 Jon US"
Source: Combining n DataTables into a Single DataTable
Last Access: 04-August-2016
First you can filter right column in to separate data datable
DataTable filterDatatable1= new DataTable();
DataTable filterDatatable2= new DataTable();
DataTable filterDatatable1= OriginaDatatable1.DefaultView.ToTable(false, "ColumnName1", "ColimnName2");
DataTable filterDatatable1= OriginaDatatable1.DefaultView.ToTable(false, "ColumnName1", "ColimnName2", "ColimnName3");
Then you can use merge function
DataTable dtAll = new DataTable();
dtAll.Merge(filterDatatable1);
dtAll.Merge(filterDatatable2);

Grouping Row In Datatable

I have data like this..
ID
1234-001
1234-002
1234-003
5678-001
7890-001
7890-002
I am holding this data in a datatable. I am attempting to do some processing on the rows by groups based on the base number i.e. 1234, 5678, 7890
How can I iterate through this datatable and hold in new (temp) datatable
1234-001,1234-002, 1234-003
clear the temp datatable then hold
5678-001
clear the temp datatable then hold
7890-001,7890-002
I am working on an old code base and LINQ is not available. I cant come up with an elegant solution. Maybe something to do with dataviews im not sure?
You say you don't want to use LINQ but would prefer an elegant solution... Unless I am missing something vital in your question, this LINQified code seems to let you do what you want.
var grouped = from d in data
group d by d.Id.Split('-').FirstOrDefault();
foreach(var g in grouped) {
// do something with each group
}
Non-LINQ, non-var answer:
DataTable data = new DataTable();
data.Columns.Add("ID");
data.Columns.Add("Value");
data.Rows.Add("1234-001", "Row 1");
data.Rows.Add("1234-002", "Row 2");
data.Rows.Add("1234-003", "Row 3");
data.Rows.Add("5678-001", "Row 4");
data.Rows.Add("7890-001", "Row 5");
data.Rows.Add("7890-002", "Row 5");
Dictionary<String, List<DataRow>> grouped = new Dictionary<String, List<DataRow>>();
foreach(DataRow r in data.Select()) {
List<DataRow> groupedRows;
String key = r["ID"].ToString().Split('-')[0];
if(!grouped.TryGetValue(key, out groupedRows)) {
groupedRows = new List<DataRow>();
grouped[key] = groupedRows;
}
groupedRows.Add(r);
}
foreach(KeyValuePair<String, List<DataRow>> g in grouped) {
String groupKey = g.Key;
Console.WriteLine(groupKey);
foreach(DataRow r in g.Value) {
Console.WriteLine("\t{0}", r["Value"]);
}
}
I get the following output, so I'm not seeing "it only groups the first 3 and stops":
1234
Row 1
Row 2
Row 3
5678
Row 4
7890
Row 5
Row 5
Here is a non Linq example. Since you say it's sorted you can do it in one loop.
DataTable dt1 = new DataTable();
dt1.Columns.Add("ID", typeof (string));
dt1.Rows.Add("1234-001");
dt1.Rows.Add("1234-002");
dt1.Rows.Add("1234-003");
dt1.Rows.Add("5678-001");
dt1.Rows.Add("7890-001");
dt1.Rows.Add("7890-002");
int i = 0;
while (i < dt1.Rows.Count)
{
DataRow row = dt1.Rows[i];
string key = row.Field<string>("ID").Split('-')[0];
DataView dv = new DataView(dt1);
dv.RowFilter = String.Format("ID LIKE '{0}*'", key.Replace("'", "''"));
DataTable tempdt = dv.ToTable();
i = i + tempdt.Rows.Count;
}
Does this help some?
DataTable dt = new DataTable();
dt.Columns.Add("Data", typeof(string));
dt.Rows.Add("1234-001");
dt.Rows.Add("1234-002");
dt.Rows.Add("1234-003");
dt.Rows.Add("5678-001");
dt.Rows.Add("7890-001");
dt.Rows.Add("7890-002");
var stuff = from dr in dt.Select()
group dr by dr["Data"].ToString().Split('-')[0] into g
select new {First = g.Key, Records = g.ToList()};
stuff.Dump();
var groups = table.AsEnumerable();
List<List<DataRow>> groupList = (from g in table.AsEnumerable()
group g by g.Field<string>("id").ToString().Split('-').First() into Group1
select Group1.ToList()).ToList();

Combining n DataTables into a Single DataTable

All, there are some question on this, but I can't seem to extract enough information to solve the problem for my case. I extract an unknown number of tables into SQL Server 'Tab1', 'Tab2', 'Tab3', ... , 'TabN'. The columns in these tables are different, but the row definitions are the same. I need to pull all the data in from the Server into N DataTables and then combine these to form a single DataTable. What I do currently is
int nTmpVolTabIdx = 1;
strSqlTmp = String.Empty;
using (DataTable dataTableALL = new DataTable())
{
while (true)
{
string strTmpVolName = String.Format("Tab{0}", nTmpVolTabIdx);
strSqlTmp = String.Format("SELECT * FROM [{0}];", strTmpVolName);
// Pull the data from 'VolX' into a local DataTable.
using (DataTable dataTable = UtilsDB.DTFromDB(conn, strTmpVolName, strSqlTmp, false))
{
if (dataTable == null)
break;
else
dataTableALL.Merge(dataTable);
}
nTmpVolTabIdx++;
}
...
}
This merges the DataTables but they are miss-aligned (padding blank cells onto the appended data set). I could append the columns of the new DataTable via a loop; but is there an easier/nicer way to do this (perhaps using LINQ)?
Thanks for your time.
Edit. To provide the example data sets.
What I required is
The individual tables are
After the first Merge operation I have the following
Thanks again.
The table has repeating primary keys after the Merge because no primary-key was defined. So either specify the PK or try this method here which i've written from scratch(so it's not really tested):
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)
{
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;
}
You can call it in this way:
var tables = new[] { tblA, tblB, tblC };
DataTable TblUnion = tables.MergeAll("c1");
Used this sample data:
var tblA = new DataTable();
tblA.Columns.Add("c1", typeof(int));
tblA.Columns.Add("c2", typeof(int));
tblA.Columns.Add("c3", typeof(string));
tblA.Columns.Add("c4", typeof(char));
var tblB = new DataTable();
tblB.Columns.Add("c1", typeof(int));
tblB.Columns.Add("c5", typeof(int));
tblB.Columns.Add("c6", typeof(string));
tblB.Columns.Add("c7", typeof(char));
var tblC = new DataTable();
tblC.Columns.Add("c1", typeof(int));
tblC.Columns.Add("c8", typeof(int));
tblC.Columns.Add("c9", typeof(string));
tblC.Columns.Add("c10", typeof(char));
tblA.Rows.Add(1, 8500, "abc", 'A');
tblA.Rows.Add(2, 950, "cde", 'B');
tblA.Rows.Add(3, 150, "efg", 'C');
tblA.Rows.Add(4, 850, "ghi", 'D');
tblA.Rows.Add(5, 50, "ijk", 'E');
tblB.Rows.Add(1, 7500, "klm", 'F');
tblB.Rows.Add(2, 900, "mno", 'G');
tblB.Rows.Add(3, 150, "opq", 'H');
tblB.Rows.Add(4, 850, "qrs", 'I');
tblB.Rows.Add(5, 50, "stu", 'J');
tblC.Rows.Add(1, 7500, "uvw", 'K');
tblC.Rows.Add(2, 900, "wxy", 'L');
tblC.Rows.Add(3, 150, "yza", 'M');
tblC.Rows.Add(4, 850, "ABC", 'N');
tblC.Rows.Add(5, 50, "CDE", 'O');
After DataTable.Merge in MergeAll:
After some modifications to join the rows in MergeAll:
Update
Since this question arose in one of the comments, if the only relation between two tables is the index of a DataRow in the table and you want to merge both tables according to the index:
public static DataTable MergeTablesByIndex(DataTable t1, DataTable t2)
{
if (t1 == null || t2 == null) throw new ArgumentNullException("t1 or t2", "Both tables must not be null");
DataTable t3 = t1.Clone(); // first add columns from table1
foreach (DataColumn col in t2.Columns)
{
string newColumnName = col.ColumnName;
int colNum = 1;
while (t3.Columns.Contains(newColumnName))
{
newColumnName = string.Format("{0}_{1}", col.ColumnName, ++colNum);
}
t3.Columns.Add(newColumnName, col.DataType);
}
var mergedRows = t1.AsEnumerable().Zip(t2.AsEnumerable(),
(r1, r2) => r1.ItemArray.Concat(r2.ItemArray).ToArray());
foreach (object[] rowFields in mergedRows)
t3.Rows.Add(rowFields);
return t3;
}
Sample:
var dt1 = new DataTable();
dt1.Columns.Add("ID", typeof(int));
dt1.Columns.Add("Name", typeof(string));
dt1.Rows.Add(1, "Jon");
var dt2 = new DataTable();
dt2.Columns.Add("Country", typeof(string));
dt2.Rows.Add("US");
var dtMerged = MergeTablesByIndex(dt1, dt2);
The result table contains three columns ID,Name,Country and a single row: 1 Jon US

Categories

Resources