So I have a query that joins two data tables together:
var results = (
from t1 in table1.AsEnumerable()
join t2 in table2.AsEnumerable() on
new { a = t1["col1"], b= t1["col2"], c = t1["col3"] } equals
new { a= t2["col1"], b= t2["col2"], c= t2["col3"] }
into joinedComboTable
select joinedComboTable);
and it produces a result whose type is IEnumerable<IEnumerable<datarow>>"
How am I to convert that to a DataTable? Tables 1 and Tables 2 are C# DataTables. I do see 304 results which I can see through the debugger and the results.inner (Non-Public) parameter that I have DataColumns and I can see 304 rows. But am unable to figure out how to get to actual result and have it saved into a DataTable.
UPDATE: 2020.01.23 # 9:04pm
So, I checked out a couple options below and when I perform a results.ToList(), I get basically a list of 304 entries, but each row value is System.Data.DataRow[0]. I must be missing something....
Iterating over this doesn't produce the desired results.
Try This
static void Main(string[] args)
{
var table1 = new DataTable();
table1.Columns.Add("col1", typeof(string));
table1.Columns.Add("col2", typeof(string));
table1.Columns.Add("col3", typeof(string));
table1.Columns.Add("col4", typeof(string));
var row = table1.NewRow();
row["col1"] = "1";
row["col2"] = "1";
row["col3"] = "1";
row["col4"] = "something different";
table1.Rows.Add(row);
row = table1.NewRow();
row["col1"] = "2";
row["col2"] = "2";
row["col3"] = "2";
row["col4"] = "something different";
table1.Rows.Add(row);
var table2 = new DataTable();
table2.Columns.Add("col1", typeof(string));
table2.Columns.Add("col2", typeof(string));
table2.Columns.Add("col3", typeof(string));
table2.Columns.Add("col4", typeof(string));
row = table2.NewRow();
row["col1"] = "1";
row["col2"] = "1";
row["col3"] = "1";
row["col4"] = "Another different thing";
table2.Rows.Add(row);
var results = (
from t1 in table1.AsEnumerable()
join t2 in table2.AsEnumerable() on
new { a = t1["col1"], b = t1["col2"], c = t1["col3"] } equals
new { a = t2["col1"], b = t2["col2"], c = t2["col3"] }
into joinedComboTable
select joinedComboTable).ToList();
//Result
var newTable = results.FirstOrDefault()?.CopyToDataTable();
//However to get col4 form table 2 you need to do this
var result2 = (
from t1 in table1.AsEnumerable()
join t2 in table2.AsEnumerable() on
new { a = t1["col1"], b = t1["col2"], c = t1["col3"] } equals
new { a = t2["col1"], b = t2["col2"], c = t2["col3"] }
select new { a = t1["col1"], b = t1["col2"], c = t1["col3"], d = t1["col4"], e = t2["col4"] });
//Result
var newTable2 = table1.Clone();
newTable2.Columns.Add("col4FromTable2", typeof(string));
foreach (var x1 in result2)
{
var r = newTable2.NewRow();
r["col1"] = x1.a;
r["col2"] = x1.b;
r["col3"] = x1.c;
r["col4"] = x1.d;
r["col4FromTable2"] = x1.e;
newTable2.Rows.Add(r);
}
}
You can get the first level by calling var rows = result.FirstOrDefault() to return an IEnumerable of rows. Create a new instance of the data table var newtable = new DataTable(); then loop through to add the rows to the rows collection property of the new data table like so...
for(var i=0; i <= rows.Count(); i++)
newtable.Rows.Add(rows[i]);
something like this should work for you.
Sorry for the typo.. I'm using my mobile phone
Related
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.
this question has been asked but mine has a different approach link 1
l have a datatable with the following data
DataTable dtProduct = new DataTable();
dtProduct.Columns.Add("productId");
dtProduct.Columns.Add("name");
DataRow dataRow = dtProduct.NewRow();
dataRow["productId"] = 1;
dataRow["name"] = "Burger";
dtProduct.Rows.Add(dataRow);
DataRow dataRow2 = dtProduct.NewRow();
dataRow2["productId"] = 2;
dataRow2["name"] = "Chicken";
dtProduct.Rows.Add(dataRow2);
DataTable dtSales = new DataTable();
dtSales.Columns.Add("productId");
dtSales.Columns.Add("saleId");
dtSales.Columns.Add("month");
dtSales.Columns.Add("quantity");
DataRow dataSalesRow = dtSales.NewRow();
dataSalesRow["productId"] = 1;
dataSalesRow["saleId"] = "1";
dataSalesRow["month"] = "Jan";
dataSalesRow["quantity"] = 3433;
dtSales.Rows.Add(dataSalesRow);
DataRow drSales2 = dtSales.NewRow();
drSales2["productId"] = 1;
drSales2["saleId"] = "2";
drSales2["month"] = "Feb";
drSales2["quantity"] = 56;
dtSales.Rows.Add(drSales2);
DataRow drSales3 = dtSales.NewRow();
drSales3["productId"] = 1;
drSales3["saleId"] = "3";
drSales3["month"] = "Mar";
drSales3["quantity"] = 34522;
dtSales.Rows.Add(drSales3);
DataRow drSales4 = dtSales.NewRow();
drSales4["productId"] = 2;
drSales4["saleId"] = "4";
drSales4["month"] = "Feb";
drSales4["quantity"] = 345;
dtSales.Rows.Add(drSales4);
And another sample 2
DataTable dtStudents = new DataTable();
dtStudents.Columns.Add("studentId");
dtStudents.Columns.Add("fullname");
DataRow drStudentrow = dtStudents.NewRow();
drStudentrow["studentId"] = 1;
drStudentrow["fullname"] = "Bil";
dtStudents.Rows.Add(drStudentrow);
DataRow drStudentrow2 = dtStudents.NewRow();
drStudentrow2["studentId"] = 2;
drStudentrow2["fullname"] = "Paul";
dtStudents.Rows.Add(drStudentrow2);
DataTable dtStudentExam = new DataTable();
dtStudentExam.Columns.Add("studentId");
dtStudentExam.Columns.Add("subjectId");
dtStudentExam.Columns.Add("mark");
DataRow dataStudentExamRow = dtStudentExam.NewRow();
dataStudentExamRow["studentId"] = 1;
dataStudentExamRow["subjectId"] = "E123";
dataStudentExamRow["mark"] = 34;
dtStudentExam.Rows.Add(dataStudentExamRow);
DataRow dataStudentExamRow2 = dtStudentExam.NewRow();
dataStudentExamRow2["studentId"] = 2;
dataStudentExamRow2["subjectId"] = "E123";
dataStudentExamRow2["mark"] = 90;
dtStudentExam.Rows.Add(dataStudentExamRow2);
DataRow dataStudentExamRow3 = dtStudentExam.NewRow();
dataStudentExamRow3["studentId"] = 1;
dataStudentExamRow3["subjectId"] = "E155";
dataStudentExamRow3["mark"] = 78;
dtStudentExam.Rows.Add(dataStudentExamRow3);
DataRow dataStudentExamRow4 = dtStudentExam.NewRow();
dataStudentExamRow4["studentId"] = 1;
dataStudentExamRow4["subjectId"] = "E101";
dataStudentExamRow4["mark"] = 12;
dtStudentExam.Rows.Add(dataStudentExamRow4);
DataRow dataStudentExamRow5 = dtStudentExam.NewRow();
dataStudentExamRow5["studentId"] = 1;
dataStudentExamRow5["subjectId"] = "E234";
dataStudentExamRow5["mark"] = 42;
dtStudentExam.Rows.Add(dataStudentExamRow5);
DataTable products(dtProduct ) has a productId which is the key and another data table sales (dtSales) has sales for the product based on month . and has a column productId .
what l want to achieve is merge this two tables to this
Same Applies for students
l have a data table student (dtStudents) but l however l have student exam marks data tables (dtStudentExam) for each subject
and combine to something like this
So my question is can l do this dynamically . l tried the following below
public static System.Data.DataTable MergeRowsToColumns(DataTable rowDataTable, string rowDataTableIdColumnName, string friendlyName, DataTable columData)
{
List<string> columnsToAdd = new List<string>();
if (rowDataTable == null)
{
return null;
}
DataTable finalDataTable = new DataTable();
finalDataTable.Columns.Add(friendlyName.ToLower());
finalDataTable.Columns.Add(rowDataTableIdColumnName.ToLower());
foreach (DataColumn column in columData.Columns)
{
if (column.ColumnName.ToString() == rowDataTableIdColumnName.ToString())
{
continue;
}
else
{
finalDataTable.Columns.Add(column.ColumnName.ToString());
columnsToAdd.Add(column.ColumnName.ToString());
}
}
foreach (DataRow row in rowDataTable.Rows)
{
DataRow newRow = finalDataTable.NewRow();
newRow[rowDataTableIdColumnName] = row[rowDataTableIdColumnName];
newRow[friendlyName] = row[friendlyName];
foreach (DataRow columnRows in columData.Rows)
{
foreach (string column in columnsToAdd)
{
var value = columnRows[column].ToString();
newRow[column] = value;
}
}
finalDataTable.Rows.Add(newRow);
}
return finalDataTable;
}
Algorithm logic
Table 1 holds the key information
Table 2 holds the value data for table
using a column name from table one for id which will be the same on table 2
can l do it generically
in one function like
var dtMergedProducts = HelperDataTable.MergeRowsToColumns(dtProduct, "productId", "name", dtSales);
var dtStudents = HelperDataTable.MergeRowsToColumns(dtStudents, "studentId", "fullname", dtStudentExam);
Please am retrieving the data as is hence can not group by in table 2 and also table two does not have a friendly name that will be displayed.
Most of the data that is loaded is not more than 100 000
this is the link of the helper utility am making git hub
Edit One
DataTable 2 will have column value one for column and the other for value
For example
dtSales
1- month : shown vertically
2- quantity: will be cell value
Sample 2
dtStudentExam
1- subjectId : shown vertically
2- mark : will be the student mark
l got it working this morning
here if the method
public static System.Data.DataTable MergeRowsToColumns(DataTable rowDataTable, string rowDataTableIdColumnName, string friendlyName, DataTable dataAllValue , string dataColumnValue , string dataColumnKey)
{
List<string> columnsToAdd = new List<string>();
if (rowDataTable == null)
{
return null;
}
DataTable finalDataTable = new DataTable();
finalDataTable.Columns.Add(friendlyName.ToLower());
finalDataTable.Columns.Add(rowDataTableIdColumnName.ToLower());
foreach (DataRow row in dataAllValue.Rows)
{
if (row[rowDataTableIdColumnName].ToString() == rowDataTableIdColumnName.ToString())
{
continue;
}
else
{
var isExistColumnValue = columnsToAdd.Where(c => c == row[dataColumnKey].ToString()).FirstOrDefault();
if(isExistColumnValue == null)
{
columnsToAdd.Add(row[dataColumnKey].ToString());
finalDataTable.Columns.Add(row[dataColumnKey].ToString());
}
}
}
foreach (DataRow row in rowDataTable.Rows)
{
DataRow newRow = finalDataTable.NewRow();
newRow[rowDataTableIdColumnName] = row[rowDataTableIdColumnName];
newRow[friendlyName] = row[friendlyName];
foreach (DataRow columnRows in dataAllValue.Rows)
{
if(row[rowDataTableIdColumnName].ToString() != columnRows[rowDataTableIdColumnName].ToString())
{
continue;
}
var columnName = columnRows[dataColumnKey].ToString();
var columnValue = columnRows[dataColumnValue].ToString();
newRow[columnName] = columnValue;
}
finalDataTable.Rows.Add(newRow);
}
return finalDataTable;
}
it looked like l was failing on adding the column names before merging
you can just call like
var dt = HelperDataTable.MergeRowsToColumns(dtProduct, "productId", "name", dtSales , "quantity", "month");
var dt2 = HelperDataTable.MergeRowsToColumns(dtStudents, "studentId", "fullname", dtStudentExam , "mark", "subjectId");
how can i use dynamic columns instead of this query
id = row.Field<int>("id") ,
rec_date = row.Field<string>("rec_date")
var result = from row in dt.AsEnumerable()
group row by new
{
id = row.Field<int>("id") ,
rec_date = row.Field<string>("rec_date")
} into section1
select new
{
section1.Key.id,
section1.Key.rec_date,
children = from l2 in section1
select new
{
tax_rate = l2.Field<string>("tax_rate"),
tax_amount = l2.Field<string>("tax_amount")
}
};
var jsonString = JsonConvert.SerializeObject(result);
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.
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: