How to join 2 data tables - c#

DataTable1
LoginId LoginName SCount
1 Mohit 20
3 Riya 25
DataTable2
LoginId LoginName ECount
2 Smita 11
3 Riya 13
I want to show result like this
LoginName Scount Ecount Total
Mohit 20 0 20
Smita 0 11 11
Riya 25 13 38
Query:
DataTable dtResult = new DataTable();
DataTable UserCount1 = new DataTable();
DataTable UserCount2 = new DataTable();
// Assigning value to datatable
if (ds != null)
{
UserCount1 = ds.Tables["UserCount1"];
UserCount2 = ds.Tables["UserCount2"];
}
var LinqResult =
from dataRows1 in UserCount1.AsEnumerable()
join dataRows2 in UserCount2.AsEnumerable()
on dataRows1.Field<string>("LoginId") equals dataRows2.Field<string>("LoginId") into lj
from r in lj.DefaultIfEmpty()
select dtResult.LoadDataRow(new object[]
{
dataRows2.Field<string>("LoginName"),
r == null ? 0 : r.Field<int>("SCount"),
r == null ? 0 : r.Field<int>("ECount")
}, false);
Getting complie time error in
select statement( dataRows2.Field<string>("LoginName"),)
that dataRows2 does not exist in current context.
How to achieve that result?

For the easy and strongly typed solution, I would strongly suggest defining classes, such as:
class User1 { public int LoginId; public string LoginName; public int SCount; }
class User2 { public int LoginId; public string LoginName; public int ECount; }
to enable LINQ extension methods, then your task becomes quite easy (explanation in comments in code):
// Sample data.
DataTable UserCount1 = new DataTable();
DataTable UserCount2 = new DataTable();
UserCount1.Columns.AddRange(new DataColumn[] { new DataColumn("LoginId"), new DataColumn("LoginName"), new DataColumn("SCount") });
UserCount2.Columns.AddRange(new DataColumn[] { new DataColumn("LoginId"), new DataColumn("LoginName"), new DataColumn("ECount") });
UserCount1.Rows.Add(1, "Mohit", 20);
UserCount1.Rows.Add(3, "Riya", 25);
UserCount2.Rows.Add(2, "Smita", 31);
UserCount2.Rows.Add(3, "Riya", 13);
// Here we create lists of our users.
List<User1> users1 = new List<User1>();
List<User2> users2 = new List<User2>();
foreach (DataRow row in UserCount1.Rows)
users1.Add(new User1() { LoginId = int.Parse(row["LoginId"].ToString()), LoginName = (string)row["LoginName"], SCount = int.Parse(row["SCount"].ToString()) });
foreach (DataRow row in UserCount2.Rows)
users2.Add(new User2() { LoginId = int.Parse(row["LoginId"].ToString()), LoginName = (string)row["LoginName"], ECount = int.Parse(row["ECount"].ToString()) });
// Full outer join: first we join, then add entries, that were not included.
var result = users1.Join(users2, u1 => u1.LoginId, u2 => u2.LoginId, (u1, u2) => new { LoginId = u1.LoginId, LoginName = u1.LoginName, SCount = u1.SCount, ECount = u2.ECount, Total = u1.SCount + u2.ECount }).ToList();
result.AddRange(users1.Where(u1 => !result.Select(u => u.LoginId).Contains(u1.LoginId)).Select(u1 => new { LoginId = u1.LoginId, LoginName = u1.LoginName, SCount = u1.SCount, ECount = 0, Total = u1.SCount }));
result.AddRange(users2.Where(u2 => !result.Select(u => u.LoginId).Contains(u2.LoginId)).Select(u2 => new { LoginId = u2.LoginId, LoginName = u2.LoginName, SCount = 0, ECount = u2.ECount, Total = u2.ECount }));
Then you can construct another result DataTable, for which I don't see any reason.

Related

LINQ only sum rows if columns totaled is not zero

Given the following rows:
Amount, Name, Id
Scenario 1: 100.00,ABC,101
-100.00,ABC,101
Scenario 2: 50.00,XYZ,123
-100.00,XYZ,123
I want to sum and group the rows only if the the amount does not totaled to 0.00 amount. So the Linq query should return this:
Amount, Name, Id
Scenario 1: 100.00,ABC,101
-100.00,ABC,101
Scenario 2:-50.00,XYZ,123
What I have so far:
var results = dt.AsEnumerable().GroupBy(row => new
{
Name = row.Field<string>("NAME"),
Id = row.Field<int>("ID")
}).Select(grp =>
{
DataRow dr = dt.NewRow();
dr["AMOUNT"] = grp.Sum(r => r.Field<decimal>("AMOUNT"));
dr["NAME"] = grp.Key.Name;
dr["ID"] = grp.Key.Id;
return dr;
}).CopyToDataTable();
You could try the following query using SelectMany extension method:
var query= dt.AsEnumerable().GroupBy(row => new
{
Name = row.Field<string>("NAME"),
Id = row.Field<int>("ID")
})
.SelectMany(grp=>
{
var sum=grp.Sum(r => r.Field<decimal>("AMOUNT");
if(sum!=0)
{
DataRow dr = dt.NewRow();
dr["AMOUNT"] = sum;
dr["NAME"] = grp.Key.Name;
dr["ID"] = grp.Key.Id;
return dr;
}
else
{
return grp;
}
}).CopyToDataTable();
It's difficult to understand what you're asking, so I'm assuming that you mean:
Sum and group rows, so only a single summarised transaction is listed for any given ID, unless the total is zero, then list all of the transactions for that ID.
Here's a working example, with the test data you provided:
var amounts = new[]
{
new
{
Amount = 100.00m,
Name = "ABC",
Id = 101,
},
new
{
Amount = -100.00m,
Name = "ABC",
Id = 101,
},
new
{
Amount = 50.00m,
Name = "XYZ",
Id = 123,
},
new
{
Amount = -100.00m,
Name = "XYZ",
Id = 123,
},
};
// summarise everything
var summaries = from a in amounts
group a by new { a.Id, a.Name } into grouping
select new
{
Amount = grouping.Sum(g => g.Amount),
grouping.Key.Name,
grouping.Key.Id,
};
// get the ids of records we need the full audit log for
var zeroSummaries = summaries.Where(s => s.Amount == 0).Select(s => s.Id).ToList();
// concat the summarised records together with the ones we need the full audit log for
summaries = amounts.Where(a => zeroSummaries.Contains(a.Id))
.Concat(summaries.Where(s => s.Amount != 0));
Here's the output:

How to join two lists and fill a datagridview

I have two list (1st with values from a website, 2nd with values from a .csv file) and I'd like to join them in another list, starting two equals values, and display it in a datagridview.
Before to post the code, I'd like to say that I tried to fill my datagridview with these two lists separately and they work.
I didn't get any error, but I can't see my datagridview with values.
I'm going to post my code and explain it.
First List Code:
var url = textBox5.Text;
//var url = "http://www.betexplorer.com/soccer/norway/tippeligaen/results/";
var web = new HtmlWeb();
var doc = web.Load(url);
Bets = new List<Bet>();
// Lettura delle righe
var Rows = doc.DocumentNode.SelectNodes("//tr");
foreach (var row in Rows)
{
if (!row.GetAttributeValue("class", "").Contains("rtitle"))
{
if (string.IsNullOrEmpty(row.InnerText))
continue;
var rowBet = new Bet();
foreach (var node in row.ChildNodes)
{
var data_odd = node.GetAttributeValue("data-odd", "");
if (string.IsNullOrEmpty(data_odd))
{
if (node.GetAttributeValue("class", "").Contains("first-cell"))
{
rowBet.Match = node.InnerText.Trim();
var matchTeam = rowBet.Match.Split(new[] { " - " }, StringSplitOptions.RemoveEmptyEntries);
rowBet.Home = matchTeam[0];
rowBet.Host = matchTeam[1];
}
if (node.GetAttributeValue("class", "").Contains("result"))
{
rowBet.Result = node.InnerText.Trim();
var matchPoints = rowBet.Result.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
int help;
if (int.TryParse(matchPoints[0], out help))
{
rowBet.HomePoints = help;
}
if (matchPoints.Length == 2 && int.TryParse(matchPoints[1], out help))
{
rowBet.HostPoints = help;
}
}
if (node.GetAttributeValue("class", "").Contains("last-cell"))
rowBet.Date = node.InnerText.Trim();
}
else
{
rowBet.Odds.Add(data_odd);
}
}
if (!string.IsNullOrEmpty(rowBet.Match))
Bets.Add(rowBet);
}
}
Second List & Combined List Code:
string FileName = #"C:\mydir\testcsv.csv";
OleDbConnection conn = new OleDbConnection
("Provider=Microsoft.Jet.OleDb.4.0; Data Source = " +
Path.GetDirectoryName(FileName) +
"; Extended Properties = \"Text;HDR=YES;FMT=Delimited\"");
conn.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter
("SELECT * FROM " + Path.GetFileName(FileName), conn);
DataSet ds = new DataSet("Temp");
adapter.Fill(ds);
conn.Close();
// DataTable dt = new DataTable();
DataTable dt = ds.Tables[0];
//dataGridView2.DataSource = dt;
// dataGridView2.DataMember = "Table";
List<HT> matchlist = new List<HT>();
matchlist = (from DataRow dr in dt.Rows
select new HT()
{
Home = dr["Home"].ToString().Replace("Milan", "AC Milan").Replace("Roma", "AS Roma"),
Host = dr["Host"].ToString().Replace("Milan", "AC Milan").Replace("Roma", "AS Roma"),
ScoreHome = dr["ScoreHome"].ToString(),
ScoreAway = dr["ScoreAway"].ToString(),
//Segno = dr["Segno"].ToString(),
//odd1 = dr["odd1"].ToString(),
//oddx = dr["oddx"].ToString(),
//odd2 = dr["odd2"].ToString()
}).ToList();
// dataGridView2.DataSource = matchlist;
var combinedDataList = (from d1 in Bets
//join d2 in dataList2 on d1.Home equals d2.Home
join d2 in matchlist on new { d1.Home, d1.Host } equals new { d2.Home, d2.Host }
select new CombinedData
{
Data = d1.Date,
Home = d1.Home,
Away = d1.Host,
HSFT = d1.HomePoints,
ASFT = d1.HostPoints,
HSHT = d2.ScoreHome,
ASHT = d2.ScoreAway,
HODD = d1.odd1,
XODD = d1.oddX,
AODD = d1.odd2,
RisFin = d1.RisFin,
Over05SH = d1.over05sh,
Over05FT = d1.Over05FT,
Over15FT = d1.Over15FT,
Over25FT = d1.Over25FT,
Over35FT = d1.Over35FT,
Over45FT = d1.Over45FT
}).OrderBy(p => p.HODD);
dataGridView2.DataSource = combinedDataList;
Thank you for your attention. Have a fantastic sunday!
EDIT: I delete unnecessary code
EDIT2: I add the screen of my single list output. Let's see:
First List:
Second List:
So, I'd like to merge "ScoreHome" and "ScoreAway" from the second list in my first list based on "Home" and "Host" that I have in both lists.

Sum of fields using LINQ

Some background before asking my question.
Im using sql compact, and i have two tables,
The first table (IssueEmp)
The second table (RecEmp)
SqlCeDataAdapter adap = new SqlCeDataAdapter("SELECT * FROM RecEmp", cn);
DataTable dat = new DataTable();
DataSet receice = new DataSet();
adap.Fill(receice);
adap.Fill(dat);
SqlCeDataAdapter adap1 = new SqlCeDataAdapter("SELECT * FROM IssueEmp", cn);
DataTable dat1 = new DataTable();
DataSet issue = new DataSet();
adap1.Fill(issue);
adap1.Fill(dat1);
Im performing a join between RecEmp and IssueEmp using linq
var res = from t1 in receice.Tables[0].AsEnumerable()
join t2 in issue.Tables[0].AsEnumerable()
on new
{
CNo = t1.Field<int>("CNo"),
Empid = t1.Field<int>("EmpID")
}
equals new
{
CNo = t2.Field<int>("CNo"),
Empid = t2.Field<int>("EmpID")
}
select new
{
SNo = t1.Field<int>("SNo"),
ChNo = t1.Field<int>("CNo"),
EmpID = t1.Field<int>("EmpID"),
DateIssued = t2.Field<DateTime>("Date"),
RMIssued = t2.Field<string>("RMCode"),
QuantityIssued = t2.Field<double>("Quantity"),
DateReceived = t1.Field<DateTime>("Date"),
RMCodeReceived = t1.Field<string>("RMCode"),
QuantityReceived = t1.Field<double>("Quantity")
};
The output Im getting from the above linq query is
But I don't know how to get the sum of issued quantity likewise the sum of received quantity, lastly the difference between the two sum as the diff. The required is below.
Note:
I´m a bit lazy so I didn´t use all the records you provided, only the first four records.
Expected result:
This is what I got:
The Linq query:
var query = from d in data
group d by new { d.DateIssued, d.EmpId, d.ChNo, d.DateReceived }
into x
select new {
Date = x.Key.DateIssued,
CNo = x.Key.ChNo,
EmpId=x.Key.EmpId,
CRi = x.Where(c=>c.RMIssued == "CR").Sum(c=>c.QuantityIssued),
SJi = x.Where(c=>c.RMIssued == "SJ").Sum(c=>c.QuantityIssued),
TTi = x.Where(c=>c.RMIssued == "TT").Sum(c=>c.QuantityIssued),
WRi = x.Where(c=>c.RMIssued == "WR").Sum(c=>c.QuantityIssued),
TotalIssued = x.Sum(c => c.QuantityIssued),
DateReceived = x.Key.DateReceived,
CRr = x.Where(c=>c.RMCodeReceived == "CR").Sum(c=>c.QuantityReceived),
SJr = x.Where(c=>c.RMCodeReceived == "SJ").Sum(c=>c.QuantityReceived),
TTr = x.Where(c=>c.RMCodeReceived == "TT").Sum(c=>c.QuantityReceived),
WRr = x.Where(c=>c.RMCodeReceived == "WR").Sum(c=>c.QuantityReceived),
TotalReceived = x.Sum(c => c.QuantityReceived),
Diff = x.Sum(c => c.QuantityIssued) - x.Sum(c => c.QuantityReceived)
};
Data used:
And this is the set of data I used to test it:
var data= new []{
new { SNo= 9, ChNo=5, EmpId=81, DateIssued=dateIssued, RMIssued="SJ", QuantityIssued=30, DateReceived=dateReceived, RMCodeReceived="SJ", QuantityReceived=20.3},
new { SNo= 10, ChNo=5, EmpId=81, DateIssued=dateIssued, RMIssued="SJ", QuantityIssued=30, DateReceived=dateReceived, RMCodeReceived="CR", QuantityReceived=9.6},
new { SNo= 11, ChNo=28, EmpId=82, DateIssued=dateIssued, RMIssued="TT", QuantityIssued=30.5, DateReceived=dateReceived, RMCodeReceived="TT", QuantityReceived=29},
new { SNo= 12, ChNo=28, EmpId=82, DateIssued=dateIssued, RMIssued="WR", QuantityIssued=10, DateReceived=dateReceived, RMCodeReceived="TT", QuantityReceived=29}
};
I recommed you use LinqPad to test it.
Good luck!

DataTable group the result in one row

I have a DataTable and want to group Name, LastName and Comment. The rest should be in the same row.
In my Code firstly i make ID's values as header and then organize the Attribute values to each ID. What I want here is to group the the same Name, Lastname and Comment with their ID values.
My first Table looks like that:
ID Name Lastmame Comment Attribute
1 kiki ha hello FF
3 lola mi hi AA
2 ka xe what UU
2 kiki ha hello SS
After I use my code:
Name Lastname Comment 1 3 2
kiki ha hello FF
lola mi hi AA
ka xe what UU
kiki ha hello SS
What I want to have is:
Name Lastname Comment 1 3 2
kiki ha hello FF SS
lola mi hi AA
ka xe what UU
My Code:
DataTable table1 = new DataTable("Kunde");
table1.Columns.Add("Comment", typeof(String));
table1.Columns.Add("Name", typeof(String));
table1.Columns.Add("Lastname", typeof(String));
DataTable comment = new DataTable("Comment");
comment.Columns.Add("ID", typeof(String));
comment.Columns.Add("Comment", typeof(String));
comment.Columns.Add("Attribute", typeof(String));
DataSet ds = new DataSet("DataSet");
ds.Tables.Add(table1);
ds.Tables.Add(comment);
object[] o1 = { "hello", "kiki", "ha" };
object[] o2 = { "hi", "lola", "mi" };
object[] o3 = { "what", "ka", "xe" };
object[] c1 = { 1, "hello", "FF" };
object[] c2 = { 3, "hi", "AA" };
object[] c3 = { 2, "what", "UU" };
object[] c4 = { 2, "hello", "SS" };
table1.Rows.Add(o1);
table1.Rows.Add(o2);
table1.Rows.Add(o3);
comment.Rows.Add(c1);
comment.Rows.Add(c2);
comment.Rows.Add(c3);
comment.Rows.Add(c4);
var results = from tb1 in comment.AsEnumerable()
join tb2 in table1.AsEnumerable()
on tb1.Field<string>("Comment") equals tb2.Field<string>("Comment")
select new
{
ID = tb1.Field<String>("ID"),
Name = tb2.Field<String>("Name"),
Lastname = tb2.Field<String>("Lastname"),
Comment = tb1.Field<String>("Comment"),
Attribute = tb1.Field<String>("Attribute"),
};
DataTable result = LINQToDataTable(results);
var products = result.AsEnumerable()
.GroupBy(c => c["ID"])
.Where(g => !(g.Key is DBNull))
.Select(g => (string)g.Key)
.ToList();
var newtable = result.Copy();
products.ForEach(p => newtable.Columns.Add(p, typeof(string)));
foreach (var row in newtable.AsEnumerable())
{
if (!(row["ID"] is DBNull)) row[(string)row["ID"]] = row["Attribute"];
}
newtable.Columns.Remove("ID");
newtable.Columns.Remove("Attribute");
var result11 = from t1 in newtable.AsEnumerable()
group t1 by new { Name = t1.Field<String>("Name"), LastName = t1.Field<String>("LastName"), Comment = t1.Field<String>("Comment"), } into grp
select new
{
Name = grp.Key.Name,
LastName = grp.Key.LastName,
Comment = grp.Key.Comment,
//Something here
};
LINQToDataTable method definition
using System.Reflection;
public DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null) return dtReturn;
foreach (T rec in varlist)
{
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition()
== typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}
Based on the comments to this other answer:
One approach would be to stuff all the variable columns in a structure (like a dictionary).
In order to do this, use the following query:
var variableColumnNames = newtable.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.Except(new[]{"Name", "Lastname", "Comment"});
var result11 = from t1 in newtable.AsEnumerable()
group t1 by new
{
Name = t1.Field<String>("Name"),
LastName = t1.Field<String>("LastName"),
Comment = t1.Field<String>("Comment"),
} into grp
select new
{
grp.Key.Name,
grp.Key.LastName,
grp.Key.Comment,
Values = variableColumnNames.ToDictionary(
columnName => columnName,
columnName => grp.Max(r => r.Field<String>(columnName)))
};
If you really need to have a variable number of properties in the class, this is not possible as far as I know, so the only plausible way to do that is to output the result to another DataTable (to which we can add as many columns as we want).
Approach #2 - using dynamic
The LINQ query:
var result11 = from t1 in newtable.AsEnumerable()
group t1 by new
{
Name = t1.Field<String>("Name"),
LastName = t1.Field<String>("LastName"),
Comment = t1.Field<String>("Comment"),
} into grp
select CreateNewDynamicObject
(
grp.Key.Name,
grp.Key.LastName,
grp.Key.Comment,
variableColumnNames.ToDictionary(
columnName => columnName,
columnName => grp.Max(r => r.Field<String>(columnName)))
);
}
the new method that creates the dynamic object:
private static dynamic CreateNewDynamicObject(
string name, string lastName, string comment, Dictionary<string, string> customProperties)
{
dynamic obj = new ExpandoObject();
obj.Name = name;
obj.LastName = lastName;
obj.Comment = comment;
foreach (var prop in customProperties)
(obj as IDictionary<string, Object>).Add(prop.Key, prop.Value ?? "");
return obj;
}
Approach #3 - outputting to a DataTable
The resulting DataTable (destinationTable) can be used as a source for a DataGridView:
var destinationTable = new DataTable();
foreach (var column in newtable.Columns.Cast<DataColumn>())
destinationTable.Columns.Add(column.ColumnName, typeof(String));
var result11 =
from t1 in newtable.AsEnumerable()
group t1 by new
{
Name = t1.Field<String>("Name"),
LastName = t1.Field<String>("Lastname"),
Comment = t1.Field<String>("Comment"),
}
into grp
select
variableColumnNames.ToDictionary(
columnName => columnName,
columnName => grp.Max(r => r.Field<String>(columnName)))
.Concat(new Dictionary<string, string>
{
{"Name", grp.Key.Name},
{"Lastname", grp.Key.LastName},
{"Comment", grp.Key.Comment}
}
).ToDictionary(x => x.Key, x => x.Value);
foreach (var row in result11)
{
var newRow = destinationTable.NewRow();
foreach (var columnName in newtable.Columns.Cast<DataColumn>().Select(c => c.ColumnName))
newRow[columnName] = row[columnName];
destinationTable.Rows.Add(newRow);
}

Datatable modify column with row

I want to modify my table in a Datatable. I know that I have to use linq and group the results.
Before:
ID Name LastName
1 Kiki ha
3 lola mi
2 ka xe
2 Kiki ha
After:
Name LastName 1 3 2
Kiki ha x x
lola mi x
ka xe x
My original code:
DataTable table1 = new DataTable("table");
table1.Columns.Add("ID", typeof(String));
table1.Columns.Add("Name", typeof(String));
table1.Columns.Add("Lastname", typeof(String));
object[] a1 = { 1, "Kiki", "ha" };
object[] a2 = { 3, "lola", "mi" };
object[] a4 = { 2, "ka", "xe" };
object[] a5 = { 2, "kiki", "ha" };
table1.Rows.Add(a1);
table1.Rows.Add(a2);
table1.Rows.Add(a4);
table1.Rows.Add(a5);
I also tried this but it didn't work:
var result = from t1 in table1.AsEnumerable()
group t1 by new {ID = t1.Field<String>("ID")} into grp
select new
{
ID = grp.Key.ID,
//something must be there
};
DataGridView1.DataSource = result.ToList();
This should do what you need:
var nameGroups = from row in table1.AsEnumerable()
group row by new
{
Name = row.Field<string>("Name").ToLower(),
LastName = row.Field<string>("Lastname").ToLower(),
} into NameGroups
select NameGroups;
var tblOut = new DataTable();
tblOut.Columns.Add("Name");
tblOut.Columns.Add("LastName");
var distinctIDs = table1.AsEnumerable()
.Select(r => r.Field<string>("ID"))
.Distinct();
foreach (var id in distinctIDs)
tblOut.Columns.Add(id);
foreach (var grp in nameGroups)
{
var row = tblOut.Rows.Add();
row.SetField<string>("Name", grp.Key.Name);
row.SetField<string>("LastName", grp.Key.LastName);
foreach (DataColumn idCol in tblOut.Columns.Cast<DataColumn>().Skip(2))
{
bool userHasID = grp.Any(r => r.Field<string>("ID") == idCol.ColumnName);
row.SetField<string>(idCol, userHasID ? "x" : "");
}
}
Note that i output the lowercase names because i needed to group by case insensitive.
Edit: Here's a screenshot of the DataTable in the debugger window:

Categories

Resources