DataTable Linq join many columns - c#

I have a trouble with Linq Join. I want to join 2 tables, which they have same structure with n-columns. My problem is i don't know the names of those columns, so how can i rewrite those in select new?
Table 1: Here I have some parameters in ID, Name and LastName. Comment, Attribute and the rest are null
ID Name LastName Comment Attribute ...
"what" "ABC" ...
"hello" "SDE" ...
3 lola
1 de
4 miki
... ... ... ...
Table 2: Here is the same like Table 1 but it has some parameters in Comment, Attribute and the Rest.
ID Name LastName Comment Attribute ...
"what" "ABC" ...
"hello" "SDE" ...
1 de "hi"
4 miki "OKK"
3 lola "yo" "LL"
Result: I would like to have joined Table like this
ID Name LastName Comment Attribute ...
"what" "ABC" ...
"hello" "SDE" ...
3 lola "yo" "LL"
1 de "hi"
4 miki "OKK"
... ... ... ... ... ...
My Code would be:
var Result= from tb1 in table1.AsEnumerable()
join tb2 in tabl2.AsEnumerable()
on new
{
Name = tb1.Field<String>("Name"),
LastName = tb1.Field<String>("LastName"),
} equals new
{
Name=tb2.Field<String>("Name"),
LastName=tb2.Field<String>("LastName"),
}
into grp1
from tb3 in grp1.DefaultIfEmpty()
select new
{
ID = tb1.Field<String>("ID"),
Name = tb1.Field<String>("Name") ,
LastName = tb1.Field<String>("LastName"),
Comment = tb3!= null ? tb3.Field<String>("Comment") : null,
Attribute= tb3!= null ? tb3.Field<String>("Attribute") : null,
...
// Here should be next Columns Name but don't know how to put there
};
I tried with this code but my compiler just hanged out, dont know why
for (int i = 2; i < table1.Rows.Count; i++)
{
foreach (DataRow dr in table2.Rows)
{
if ((table1.Rows[i]["Name"].ToString() == dr["Name"].ToString())&&table1.Rows[i]["LastName"].ToString() == dr["LastName"].ToString())
{
table1.Rows.RemoveAt(i);
table1.ImportRow(dr);
}
}
}
dataGridView1.DataSource = table1;

For each row1 in table1, if there is a matching row2 in table2, use row2. Otherwise, use row1.
var newTable = table1.Clone();
foreach (DataRow row1 in table1.Rows) // To skip the first 2, use table1.Rows.Cast<DataRow>().Skip(2)
{
var row = table2.Rows.Cast<DataRow>().FirstOrDefault(row2 =>
row1["Name"].ToString() == row2["Name"].ToString() &&
row1["LastName"].ToString() == row2["LastName"].ToString()) ?? row1;
newTable.ImportRow(row);
}
dataGridView1.DataSource = newTable;

How about joining as you did and then copying the three known fields from table1's rows to table2's rows?
var copiedTable2 = table2.Copy(); // Copy table2 if you don't want it to be modified
var items = from tb1 in table1.AsEnumerable()
join tb2 in copiedTable2.AsEnumerable()
on new
{
Name = tb1.Field<String>("Name"),
LastName = tb1.Field<String>("LastName"),
} equals new
{
Name=tb2.Field<String>("Name"),
LastName=tb2.Field<String>("LastName"),
}
into grp1
from tb3 in grp1.DefaultIfEmpty()
select new
{
ID = tb1.Field<String>("ID"),
Name = tb1.Field<String>("Name") ,
LastName = tb1.Field<String>("LastName"),
Row = tb3 ?? table2.NewRow();
};
foreach(var item in items)
{
item.Row.SetField<String>("ID", item.ID);
item.Row.SetField<String>("Name", item.Name);
item.Row.SetField<String>("LastName", item.LastName);
}
var rows = items.Select(x => x.Row);
// How to set the rows as a DataGridView's DataSource
var result = table2.Clone();
foreach(var row in rows)
{
result.Rows.Add(row.ItemArray);
}
dataGridView.DataSource = result;

Try this, no need of using loops
var resultTable = from tb1 in table1.AsEnumerable()
join tb2 in tabl2.AsEnumerable()
on tb1["Name"].ToString() equals tb2["Name"].ToString()
where tb1["LastName"].ToString() == tb2["LastName"].ToString()
select r;
DataTable resultTable =result.CopyToDataTable();

Related

Returning Dictionary <string,List<string> with Linq from DataTable

i'm new in LINQ i want to group data from a DataTable with a specific column field1 value
Like this Dictionary<string,List <row>>
I tried this code
var results = from e in data.AsEnumerable()
group e by e.Field<string>("field1") into g
select new { e.Field<string>("field1"), g.ToList() } ;
I don't want to do this operation with foreach statement
Im doing it like this and i want it with LINQ
foreach (DataRow row in data.Rows)
{
string field1Val = row.Filed<string>("field1");
if (!sotrtedResult.ContainsKey(field1Val ))
{
sotrtedResult.Add(field1Val , new List<DataRow>() { row });
}
else
{
sotrtedResult[field1Val].Add(row);
}
}
You could try the following snippet:
var results = (from e in data.AsEnumerable()
group e by e.Field<string>("field1") into g
select new
{
field1 = g.Key,
values = g.Select(r=>r).ToList()
}).ToDictionary(x=>x.field1, x=>x.values);
When you group by a field, you could access it, when you make the projection as g.Key.

How to use Inner Join and then fill a DataSet with result of the join?

Well, this is my question. In short terms; I have two tables, Consequents and Atomic propositions:
AtomicP table
ID Proposition
1 | A |
1 | B |
1 | C |
2 | D |
2 | E |
Consequent Table
ID | Consequent |
1 | A |
2 | B |
And all I just want to do, is to implement a inner join which gives me all the values where the ID for both tables is the same(i.e):
AtomicP Table "A" "B" "C" -> "A" Consequent Table
and withe result given tanks to the inner joins , save that result in a Data Set or in another data structure that could be better.
Best regards.
Assuming the destination table has the values Id, Proposition and Consequent ..
insert into newtable (id,proposition,consequent) select id,atomicP,Consequent from atmicp,consequent where atomicP.id = consequent.id
public class Proposition
{
public int Id;
public string Value;
public Proposition(int id, string value){
Id = id;
Value = value;
}
}
public class Consequent
{
public int Id;
public string Value;
public Consequent(int id, string value){
Id = id;
Value = value;
}
}
var atomicP = new List<Proposition>{
new Proposition(1, "A"),
new Proposition(1, "B"),
new Proposition(1, "C"),
new Proposition(2, "D"),
new Proposition(2, "E"),
}
var consequents = new List<Consequent>{
new Consequent(1, "A"),
new Consequent(2, "B"),
}
var query = from proposition in atomicP
join consequent in consequents on proposition.Id == consequent.Id
select proposition.Value;
return query.ToList();
use this function
private DataTable JoinDataTables(DataTable t1, DataTable t2, params Func<DataRow, DataRow, bool>[] joinOn)
{
DataTable result = new DataTable();
foreach (DataColumn col in t1.Columns)
{
if (result.Columns[col.ColumnName] == null)
result.Columns.Add(col.ColumnName, col.DataType);
}
foreach (DataColumn col in t2.Columns)
{
if (result.Columns[col.ColumnName] == null)
result.Columns.Add(col.ColumnName, col.DataType);
}
foreach (DataRow row1 in t1.Rows)
{
var joinRows = t2.AsEnumerable().Where(row2 =>
{
foreach (var parameter in joinOn)
{
if (!parameter(row1, row2)) return false;
}
return true;
});
foreach (DataRow fromRow in joinRows)
{
DataRow insertRow = result.NewRow();
foreach (DataColumn col1 in t1.Columns)
{
insertRow[col1.ColumnName] = row1[col1.ColumnName];
}
foreach (DataColumn col2 in t2.Columns)
{
insertRow[col2.ColumnName] = fromRow[col2.ColumnName];
}
result.Rows.Add(insertRow);
}
}
return result;
}
An example of how you might use this:
var test = JoinDataTables(Consequents, Atomic,
(row1, row2) =>
row1.Field<int>("ID") == row2.Field<int>("ID"));
I assume you want to join In C# and get DataTable(bit unclear in question).
Code snippets joins two DataTable using Linq and inserts to another Table.
DataTable results = new DataTable();
results.Columns.Add("ID", typeof(int));
results.Columns.Add("Proposition", typeof(string));
results.Columns.Add("Consequent", typeof(string));
var result1 = from arow in AtomicP.AsEnumerable()
join con in Consequent.AsEnumerable()
on arow.Field<int>("ID") equals con.Field<int>("ID")
select results.LoadDataRow(new object[]
{
arow.Field<int>("ID"),
arow.Field<string>("Proposition"),
con.Field<string>("Consequent")
}, false);
Now we can access results by iterating through results.
foreach(DataRow row in results.Rows)
{
foreach(DataColumn column in results.Columns)
{
//Console.WriteLine(row[column]);
}
}
Working Code

Using LINQ to get information from two tables, with a join

I have a linq statement to populate two labels. The thing is, this information comes from two tables. I Have a join to join the two tables, except i cant get my Terms and Conditions from my Campaign table. Its only picking up the RedemptionLog table columns. Anyone to help with this?
MSCDatabaseDataContext MSCDB = new MSCDatabaseDataContext();
var q = from row in MSCDB.Tbl_RedemptionLogs
join d in MSCDB.Tbl_Campaigns on row.CampaignId equals d.CampaignId
orderby row.VoucherCode descending
select row;
var SeshVoucherDisplay = q.First();
lblCode.Text = SeshVoucherDisplay.VoucherCode;
lblTerms.Text = SeshVoucherDisplay
For the SeshVoucherDisplay variable, it only picks up from the RedemptionLogs table, yet i did a join? Any help?
Try something like this :
var SupJoin = from row in MSCDB.Tbl_RedemptionLogs
join d in MSCDB.Tbl_Campaigns on row.CampaignId equals d.CampaignId
orderby row.VoucherCode descending
select new { Id = row.ID, SupplierName = row.SupplierName,
CustomerName = d.CompanyName };
The column names are just for example purpose. Put your own there. And thereafter, you can apply First on it and use that particular variable.
Hope this helps.
Well, by writing select row you asked LINQ to give back to you only row.
If you want both elements, you need to ask for both of them, e.g. by writing select new { row, d }.
In this example
var foo =
new []
{
new { Id = 1, Name = "a" },
new { Id = 2, Name = "b" },
new { Id = 3, Name = "c" }
};
var bar =
new []
{
new { Id = 1, Name = "d" },
new { Id = 2, Name = "e" },
new { Id = 3, Name = "f" }
};
var baz =
from a in foo
join b in bar on a.Id equals b.Id
select new { a, b };
var qux =
from a in foo
join b in bar on a.Id equals b.Id
select new { a, b };
In baz you'll find only a list of foos, in qux you'll find a list of both foos and their bar.
Try this:
var query = (from row in MSCDB.Tbl_RedemptionLogs
join d in MSCDB.Tbl_Campaigns on row.CampaignId equals d.CampaignId)
orderby row.VoucherCode descending
select new
{
columnname = row.columnname
});
When writing select row you relate to the row you defined in from row in MSCDB.Tbl_RedemptionLogs.
However if you want the data from both tables you have to write something similar to this:
select new {
// the properties of row
Redemption = row.redemption,
// the properties of d
Campaign = d.CampaignID // alternativly use may also use row.CampaignID, but I wanted to show you may acces all the members from d also
}

Compare two DataTables and select the rows that are not present in second table

I have two DataTables and I want to select the rows from the first one which are not present in second one
For example:
Table A
id column
1 data1
2 data2
3 data3
4 data4
Table B
id column
1 data10
3 data30
I want the result to be:
Table C
id column
2 data2
4 data4
You can use Linq, especially Enumerable.Except helps to find id's in TableA that are not in TableB:
var idsNotInB = TableA.AsEnumerable().Select(r => r.Field<int>("id"))
.Except(TableB.AsEnumerable().Select(r => r.Field<int>("id")));
DataTable TableC = (from row in TableA.AsEnumerable()
join id in idsNotInB
on row.Field<int>("id") equals id
select row).CopyToDataTable();
You can also use Where but it'll be less efficient:
DataTable TableC = TableA.AsEnumerable()
.Where(ra => !TableB.AsEnumerable()
.Any(rb => rb.Field<int>("id") == ra.Field<int>("id")))
.CopyToDataTable();
I got a solution which works without LINQ:
public DataTable CompareDataTables(DataTable first, DataTable second)
{
first.TableName = "FirstTable";
second.TableName = "SecondTable";
//Create Empty Table
DataTable table = new DataTable("Difference");
try
{
//Must use a Dataset to make use of a DataRelation object
using (DataSet ds = new DataSet())
{
//Add tables
ds.Tables.AddRange(new DataTable[] { first.Copy(), second.Copy() });
//Get Columns for DataRelation
DataColumn[] firstcolumns = new DataColumn[ds.Tables[0].Columns.Count];
for (int i = 0; i < firstcolumns.Length; i++)
{
firstcolumns[i] = ds.Tables[0].Columns[i];
}
DataColumn[] secondcolumns = new DataColumn[ds.Tables[1].Columns.Count];
for (int i = 0; i < secondcolumns.Length; i++)
{
secondcolumns[i] = ds.Tables[1].Columns[i];
}
//Create DataRelation
DataRelation r = new DataRelation(string.Empty, firstcolumns, secondcolumns, false);
ds.Relations.Add(r);
//Create columns for return table
for (int i = 0; i < first.Columns.Count; i++)
{
table.Columns.Add(first.Columns[i].ColumnName, first.Columns[i].DataType);
}
//If First Row not in Second, Add to return table.
table.BeginLoadData();
foreach (DataRow parentrow in ds.Tables[0].Rows)
{
DataRow[] childrows = parentrow.GetChildRows(r);
if (childrows == null || childrows.Length == 0)
table.LoadDataRow(parentrow.ItemArray, true);
}
table.EndLoadData();
}
}
}
For more Visit http://microsoftdotnetsolutions.blogspot.in/2012/12/compare-two-datatables.html
You can use Linq Enumerable.Except Method function to get diffence between two DataTable's Here i use firstDt and secondDt,remember both Dt's have the same structure.
var EntriesNotInB = firstDt.AsEnumerable().Select(r => r.Field<string>("abc")).Except(secondDt.AsEnumerable().Select(r => r.Field<string>("abc")));
if (EntriesNotInB.Count() > 0)
{
DataTable dt = (from row in firstDt.AsEnumerable()join id in EntriesNotInB on row.Field<string>("abc") equals id select row).CopyToDataTable();
foreach (DataRow row in dt.Rows)
{
/////Place your code to manipulate on datatable Rows
}
}
To read more on Enumerable.Except Method,Go to http://msdn.microsoft.com/en-us/library/system.linq.enumerable.except(v=vs.110).aspx
and its Done!!!! Happy Coding.........

DataTable group the result

I want to group in Datatable by Name, LastName and the rest should be in same row. Can someone help me with it?
My DataTable:
Name LastName 1 3 2
kiki ha FF
lola mi AA
ka xe UU
kiki ha SS
I want to have DataTable group by Name:
Name LastName 1 3 2
kiki ha FF SS
lola mi AA
ka xe UU
My new code:
var result11 = from t1 in newtable.AsEnumerable()
group t1 by new { Name = t1.Field<String>("Name"), LastName = t1.Field<String>("LastName") } into grp
select new
{
Name = grp.Key.Name,
LastName = grp.Key.LastName,
//Something must be there
};
Add these lines instead of the comment (//something must be there):
C1 = String.Join(",", grp.Select(r=>r.Field<String>("1"))),
C2 = String.Join(",", grp.Select(r=>r.Field<String>("2"))),
C3 = String.Join(",", grp.Select(r=>r.Field<String>("3")))
to get three new columns on the output that aggregate values from the columns 1, 3 and 2.
If you have multiple values in one of the columns for a group, all the values will be shown and separated by comma (,).
If you are sure that there's at most one value per column per group, then you can simply do:
C1 = grp.Max(r => r.Field<String>("1")),
C3 = grp.Max(r => r.Field<String>("3")),
C2 = grp.Max(r => r.Field<String>("2"))
If you want a DataTable as result, this gives your desired result:
var lastNameGroups = from row in table1.AsEnumerable()
group row by new {
Name= row.Field<String>("Name"),
LastName = row.Field<String>("LastName")
} into LastNameGroups
select LastNameGroups;
var table2 = table1.Clone();
foreach (var lng in lastNameGroups)
{
var row = table2.Rows.Add();
row.SetField("Name", lng.Key.Name);
row.SetField("LastName", lng.Key.LastName);
var ones = lng.Where(r => !string.IsNullOrEmpty(r.Field<String>("1")));
if(ones.Any())
row.SetField("1", ones.First().Field<String>("1"));
var twos = lng.Where(r => !string.IsNullOrEmpty(r.Field<String>("2")));
if (twos.Any())
row.SetField("2", twos.First().Field<String>("2"));
var threes = lng.Where(r => !string.IsNullOrEmpty(r.Field<String>("3")));
if (threes.Any())
row.SetField("3", threes.First().Field<String>("3"));
}

Categories