Need to merge 2 rows in dynamic table - c#

Hi I have created dynamic table as below and in that I have 2 rows with same id
How should I merge them.
DataTable dt = new DataTable();
dt = (DataTable)Session["AddtoCart"];
DataRow dr1 = dt.NewRow();
foreach (var key in collection.AllKeys)
{
dr1["Description"] = collection["hdDescription"];
dr1["Title"] = collection["hdTitle"];
dr1["ActualQuantity"] = collection["hdactualquantity"];
dr1["PropertyId"] = collection["hdPropertyId"];
dr1["Quantity"] = collection["Quantity"];
TempData["AddedtoCart"] = ConfigurationManager.AppSettings["AddtoCart"].ToString();
}
dt.Rows.Add(dr1);
My added rows are as
Propertyid, Quantity, ActualQuantity
1 5 10
1 2 10
2 3 20
2 4 20
Th result i needed is as
Propertyid, Quantity, ActualQuantity
1 7 10
2 7 20
Update: i have tried this answer:
var query = dt.AsEnumerable()
.GroupBy(row => row.Field<int>("PropertyId"))// issue over this line
.Select(grp => new
{
PropertyId = grp.Key,
Quantity = grp.Sum(row => row.Field<int>("Quantity")),
ActualQuantity = grp.First().Field<int>("ActualQuantity"),
Title = grp.First().Field<int>("Title"),
Description = grp.First().Field<int>("Description")
});
var SumByIdTable = dt.Clone();
foreach (var x in query)
SumByIdTable.Rows.Add(x.PropertyId, x.Quantity, x.ActualQuantity,x.Title, x.Description);
Session["AddtoCart"] = SumByIdTable;
but I am getting issue as Specified cast is not valid. on .GroupBy(row => row.Field("PropertyId"))
Update 2: I have tried below code but i am getting issue
DataTable dt = new DataTable();
dt.Columns.Add("PropertyId", typeof(int));
dt.Columns.Add("Quantity", typeof(int));
dt.Columns.Add("Description", Type.GetType("System.String"));
dt.Columns.Add("Title", Type.GetType("System.String"));
dt.Columns.Add("ActualQuantity", typeof(int));
DataRow dr1 = dt.NewRow();
foreach (var key in collection.AllKeys)
{
dr1["Description"] = collection["hdDescription"];
dr1["Title"] = collection["hdTitle"];
dr1["ActualQuantity"] = Convert.ToInt32(collection["hdactualquantity"]);
dr1["PropertyId"] = Convert.ToInt32(collection["hdPropertyId"]);
dr1["Quantity"] = Convert.ToInt32(collection["Quantity"]);
TempData["AddedtoCart"] = ConfigurationManager.AppSettings["AddtoCart"].ToString();
}
dt.Rows.Add(dr1);
var query = dt.AsEnumerable()
.GroupBy(row => row.Field<int>("PropertyId"))
.Select(grp => new
{
PropertyId = grp.Key,
Quantity = grp.Sum(row => row.Field<int>("Quantity")),
ActualQuantity = grp.First().Field<int>("ActualQuantity"),
Title = grp.First().Field<string>("Title"),
Description = grp.First().Field<string>("Description")
});
var SumByIdTable = dt.Clone();
foreach (var x in query)
SumByIdTable.Rows.Add(x.PropertyId, x.Quantity, x.ActualQuantity,x.Title, x.Description);
Session["AddtoCart"] = SumByIdTable;
but I am getting issue in
SumByIdTable.Rows.Add(x.PropertyId, x.Quantity, x.ActualQuantity,x.Title, x.Description);
Input string was not in a correct format.
Resolved Update 3: I have tried below code and is working
DataTable dt = new DataTable();
dt.Columns.Add("PropertyId", typeof(int));
dt.Columns.Add("Quantity", typeof(int));
dt.Columns.Add("Description", Type.GetType("System.String"));
dt.Columns.Add("Title", Type.GetType("System.String"));
dt.Columns.Add("ActualQuantity", typeof(int));
DataRow dr1 = dt.NewRow();
foreach (var key in collection.AllKeys)
{
dr1["Description"] = collection["hdDescription"];
dr1["Title"] = collection["hdTitle"];
dr1["ActualQuantity"] = Convert.ToInt32(collection["hdactualquantity"]);
dr1["PropertyId"] = Convert.ToInt32(collection["hdPropertyId"]);
dr1["Quantity"] = Convert.ToInt32(collection["Quantity"]);
TempData["AddedtoCart"] = ConfigurationManager.AppSettings["AddtoCart"].ToString();
}
dt.Rows.Add(dr1);
var query = dt.AsEnumerable()
.GroupBy(row => row.Field<int>("PropertyId"))
.Select(grp => new
{
PropertyId = grp.Key,
Quantity = grp.Sum(row => row.Field<int>("Quantity")),
ActualQuantity = grp.First().Field<int>("ActualQuantity"),
Title = grp.First().Field<string>("Title"),
Description = grp.First().Field<string>("Description")
});
var SumByIdTable = dt.Clone();
foreach (var x in query)
SumByIdTable.Rows.Add(Convert.ToInt32(x.PropertyId), Convert.ToInt32(x.Quantity), x.Description, x.Title, Convert.ToInt32(x.ActualQuantity));
Session["AddtoCart"] = SumByIdTable;
changes was I have to add the value in SumByIdTable as clone set in dt

You can use LINQ (-to-DataTable):
var query = dt.AsEnumerable()
.GroupBy(row => row.Field<int>("Propertyid"))
.Select(grp => new {
Propertyid = grp.Key,
Quantity = grp.Sum(row => row.Field<int>("Quantity")),
ActualQuantity = grp.First().Field<int>("ActualQuantity")
});
var SumByIdTable = dt.Clone();
foreach(var x in query)
SumByIdTable.Rows.Add(x.Propertyid, x.Quantity, x.ActualQuantity);

Related

Count the specific feature in gridview using linq

I want to count the number of "d" in gridview using linq
My dataset is some thing like that :
public DataSet TitForTat()
{
DataSet ds = new DataSet();
DataTable dt = new DataTable("myt");
dt.Columns.Add(new DataColumn("iteration", typeof(int)));
dt.Columns.Add(new DataColumn("prison1", typeof(string)));
dt.Columns.Add(new DataColumn("prison2", typeof (string)));
prison[] prisons = new prison[2];
prisons[0] = new prison();
prisons[1] = new prison();
//---------------------------
DataRow dr = dt.NewRow();
prisons[0]._state = "c";
prisons[1]._state = valueOfState[rd.Next(0, 1)];
dr["iteration"] = 0;
dr["prison1"] = "c";
dr["prison2"] = prisons[1]._state;
dt.Rows.Add(dr);
//----------------------
for (int i = 1; i <= _iteration; i++)
{
prisons[0]._state = prisons[1]._state;
prisons[1]._state = valueOfState[rd.Next(0, 2)];
DataRow dr1 = dt.NewRow();
dr1["iteration"] =i;
dr1["prison1"] = prisons[0]._state;
dr1["prison2"] = prisons[1]._state;
dt.Rows.Add(dr1);
}
ds.Tables.Add(dt);
return ds;
}
And in the form i have a grid view "gvData " :
gvData.DataSource = obj.TitForTat().Tables[0];
my gridview has a column "prison1" that sometimes is "d" and sometimes is "c" .
I need to count these iteration using linq;
You can use GroupBy:
var prisonGroups = ((DataTable)gvData.DataSource).AsEnumerable()
.GroupBy(row => row.Field<string>("prison1"));
int countC = prisonGroups
.Where(g => g.Key == "c")
.Sum(g => g.Count());
int countD = prisonGroups
.Where(g => g.Key == "d")
.Sum(g => g.Count());
Here's another approach:
DataTable source = (DataTable)gvData.DataSource;
int cPrisoners = source.AsEnumerable()
.Count(row => row.Field<string>("prison1") == "c");
int dPrisoners = source.AsEnumerable()
.Count(row => row.Field<string>("prison1") == "d");

how to use group by in linq

I have one news repeater that show two fields like News_Text and News_Date.
Here I just want to display this data with group by News_date.
like:
12/24/2013
test
test1
test 2
11/12/213
blah..blah
here is my code:
private void BindNewsRepeater()
{
using (DataClassesDataContext db = new DataClassesDataContext())
{
var query = from n in db.News
orderby n.News_date descending
select new
{
News_text = n.News_text,
News_date = n.News_date
};
DataSet myDataSet = new DataSet();
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("News_text", typeof(string)));
dt.Columns.Add(new DataColumn("News_date", typeof(string)));
foreach (var item in query)
{
DataRow dr = dt.NewRow();
dr["News_text"] = item.News_text.ToString();
dr["News_date"] = item.News_date.ToString("dd/MM/yyyy");
dt.Rows.Add(dr);
}
myDataSet.Tables.Add(dt);
rpt_news.DataSource = myDataSet;
rpt_news.DataBind();
}
}
and my repeater design code is:
<asp:Repeater ID="rpt_news" runat="server" onitemdatabound="rpt_news_ItemDataBound">
<ItemTemplate><asp:Label ID="lbl_news_date" runat="server"><%#Eval("News_date")%></asp:Label><br />
<asp:Label ID="lbl_news_text" runat="server"><%#Eval("News_text")%></asp:Label>
</ItemTemplate>
<SeparatorTemplate><br/><br/></SeparatorTemplate>
</asp:Repeater>
You can simply GroupBy News_date, then select for each key of News_date a list of News_Text. Something like this:
var results = News
.GroupBy(n => n.News_date)
.Select(g => new {
NewsDate = g.Key,
NewsTexts = g.Select(x => x.News_Text)
});
This will return a list of News_Text for each NewsDate.
var query = db.News
.OrderBy(n => n.News_date)
.GroupBy(n => n.News_date, n => n.News_text);
foreach (var g in query)
{
foreach(var t in g)
{
DataRow dr = dt.NewRow();
dr["News_text"] = t.ToString();
dr["News_date"] = g.Key.ToString("dd/MM/yyyy");
dt.Rows.Add(dr);
}
}

How do I use SELECT GROUP BY in DataTable.Select(Expression)?

I try to remove the duplicate rows by select a first row from every group.
For Example
PK Col1 Col2
1 A B
2 A B
3 C C
4 C C
I want a return:
PK Col1 Col2
1 A B
3 C C
I tried following code but it didn't work:
DataTable dt = GetSampleDataTable(); //Get the table above.
dt = dt.Select("SELECT MIN(PK), Col1, Col2 GROUP BY Col1, Col2);
DataTable's Select method only supports simple filtering expressions like {field} = {value}. It does not support complex expressions, let alone SQL/Linq statements.
You can, however, use Linq extension methods to extract a collection of DataRows then create a new DataTable.
dt = dt.AsEnumerable()
.GroupBy(r => new {Col1 = r["Col1"], Col2 = r["Col2"]})
.Select(g => g.OrderBy(r => r["PK"]).First())
.CopyToDataTable();
dt = dt.AsEnumerable().GroupBy(r => r.Field<int>("ID")).Select(g => g.First()).CopyToDataTable();
dt.AsEnumerable()
.GroupBy(r => new { Col1 = r["Col1"], Col2 = r["Col2"] })
.Select(g =>
{
var row = dt.NewRow();
row["PK"] = g.Min(r => r.Field<int>("PK"));
row["Col1"] = g.Key.Col1;
row["Col2"] = g.Key.Col2;
return row;
})
.CopyToDataTable();
This solution sort by Col1 and group by Col2. Then extract value of Col2 and display it in a mbox.
var grouped = from DataRow dr in dt.Rows orderby dr["Col1"] group dr by dr["Col2"];
string x = "";
foreach (var k in grouped) x += (string)(k.ElementAt(0)["Col2"]) + Environment.NewLine;
MessageBox.Show(x);
Based on #Alfred Wallace's solution :
DataTable dt = new DataTable();
dt.Columns.Add("Col1");
dt.Columns.Add("Col2");
dt.Rows.Add("120", "34");
dt.Rows.Add("121", "34");
dt.Rows.Add("122", "34");
dt.Rows.Add("1", "345");
dt.Rows.Add("2", "345");
dt.Rows.Add("3", "345");
var grouped = from DataRow dr in dt.Rows orderby dr["Col1"] group dr by dr["Col2"];
string xxx = "", yyy = "";
foreach (var k_group in grouped)
{
xxx += (string)(k_group.ElementAt(0)["Col1"]) + Environment.NewLine;
foreach (DataRow item_dr in k_group)
{
yyy += (string)(item_dr["Col1"]) + Environment.NewLine;
// or use WhatEverMethod(item_dr);
}
var zzz = k_group.Max(g => g["Col1"]);
var qqq = k_group.Key;
}

Add columns of datatables in a dataset

I have a DataSet with 2 DataTable's. Each DataTable contains a column called "cost".
I want to calculate the sum of all costs for the 2 tables in a table called Result table, like the example below. How can I do that?
Table 1
Name | cost
balan | 6
gt | 5
Table 2
Name | cost
balan | 2
gt | 8
Result table
Name | cost
balan | 8
gt | 12
This is a way to do it:
DataTable dt1 = new DataTable();
DataTable dt2 = new DataTable();
DataTable results = new DataTable();
dt1.Columns.Add("Name");
dt1.Columns.Add("cost", typeof(int));
dt2.Columns.Add("Name");
dt2.Columns.Add("cost", typeof(int));
results.Columns.Add("Name");
results.Columns.Add("cost", typeof(int));
dt1.Rows.Add("balan", 6);
dt2.Rows.Add("balan", 2);
dt1.Rows.Add("gt", 5);
dt2.Rows.Add("gt", 8);
foreach (DataRow dr1 in dt1.Rows)
{
results.Rows
.Add(
dr1["Name"],
(int)dr1["cost"] + (int)dt2.Select(String.Format("Name='{0}'", dr1["name"]))[0]["cost"]
);
}
to get the result, you can do something like
var table1 = yourDataSet.Tables["Table 1"];
var table2 = yourDataSet.Tables["Table 2"];
var results = table1.AsEnumerable().Select(t1 => new {
name = t1.Field<string>("Name"),
cost = t1.Field<int>("cost")
})
.Concat(
table2.AsEnumerable().Select(t2 => new {
name = t2.Field<string>("Name"),
cost = t2.Field<int>("cost")
})
)
.GroupBy(m => m.name)
.Select(g => new {
name = g.Key,
cost = g.Sum(x => x.cost)
});
this won't give you a dataTable, but an IEnumerable. To transform an IEnumerable to a dataTable, see for example here
or easier, if table1 and table2 have same rows
var table1 = yourDataSet.Tables["Table 1"];
var table2 = yourDataSet.Tables["Table 2"];
var results = new DataTable();
results.Columns.Add("Name");
results.Columns.Add("cost", typeof(int));
table1.AsEnumerable().Concat(table2.AsEnumerable())
.GroupBy(m => m.Field<string>("Name"))
.Select(g => results.Rows.Add(g.Key, g.Sum(x => x.Field<int>("cost"))));

Inner join of DataTables in C#

Let T1 and T2 are DataTables with following fields
T1(CustID, ColX, ColY)
T2(CustID, ColZ)
I need the joint table
TJ (CustID, ColX, ColY, ColZ)
How this can be done in C# code in a simple way? Thanks.
If you are allowed to use LINQ, take a look at the following example. It creates two DataTables with integer columns, fills them with some records, join them using LINQ query and outputs them to Console.
DataTable dt1 = new DataTable();
dt1.Columns.Add("CustID", typeof(int));
dt1.Columns.Add("ColX", typeof(int));
dt1.Columns.Add("ColY", typeof(int));
DataTable dt2 = new DataTable();
dt2.Columns.Add("CustID", typeof(int));
dt2.Columns.Add("ColZ", typeof(int));
for (int i = 1; i <= 5; i++)
{
DataRow row = dt1.NewRow();
row["CustID"] = i;
row["ColX"] = 10 + i;
row["ColY"] = 20 + i;
dt1.Rows.Add(row);
row = dt2.NewRow();
row["CustID"] = i;
row["ColZ"] = 30 + i;
dt2.Rows.Add(row);
}
var results = from table1 in dt1.AsEnumerable()
join table2 in dt2.AsEnumerable() on (int)table1["CustID"] equals (int)table2["CustID"]
select new
{
CustID = (int)table1["CustID"],
ColX = (int)table1["ColX"],
ColY = (int)table1["ColY"],
ColZ = (int)table2["ColZ"]
};
foreach (var item in results)
{
Console.WriteLine(String.Format("ID = {0}, ColX = {1}, ColY = {2}, ColZ = {3}", item.CustID, item.ColX, item.ColY, item.ColZ));
}
Console.ReadLine();
// Output:
// ID = 1, ColX = 11, ColY = 21, ColZ = 31
// ID = 2, ColX = 12, ColY = 22, ColZ = 32
// ID = 3, ColX = 13, ColY = 23, ColZ = 33
// ID = 4, ColX = 14, ColY = 24, ColZ = 34
// ID = 5, ColX = 15, ColY = 25, ColZ = 35
I wanted a function that would join tables without requiring you to define the columns using an anonymous type selector, but had a hard time finding any. I ended up having to make my own. Hopefully this will help anyone in the future who searches for this:
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(transactionInfo, transactionItems,
(row1, row2) =>
row1.Field<int>("TransactionID") == row2.Field<int>("TransactionID"));
One caveat: This is certainly not optimized, so be mindful when getting to row counts above 20k. If you know that one table will be larger than the other, try to put the smaller one first and the larger one second.
This is my code. Not perfect, but working good. I hope it helps somebody:
static System.Data.DataTable DtTbl (System.Data.DataTable[] dtToJoin)
{
System.Data.DataTable dtJoined = new System.Data.DataTable();
foreach (System.Data.DataColumn dc in dtToJoin[0].Columns)
dtJoined.Columns.Add(dc.ColumnName);
foreach (System.Data.DataTable dt in dtToJoin)
foreach (System.Data.DataRow dr1 in dt.Rows)
{
System.Data.DataRow dr = dtJoined.NewRow();
foreach (System.Data.DataColumn dc in dtToJoin[0].Columns)
dr[dc.ColumnName] = dr1[dc.ColumnName];
dtJoined.Rows.Add(dr);
}
return dtJoined;
}
this function will join 2 tables with a known join field, but this cannot allow 2 fields with the same name on both tables except the join field, a simple modification would be to save a dictionary with a counter and just add number to the same name filds.
public static DataTable JoinDataTable(DataTable dataTable1, DataTable dataTable2, string joinField)
{
var dt = new DataTable();
var joinTable = from t1 in dataTable1.AsEnumerable()
join t2 in dataTable2.AsEnumerable()
on t1[joinField] equals t2[joinField]
select new { t1, t2 };
foreach (DataColumn col in dataTable1.Columns)
dt.Columns.Add(col.ColumnName, typeof(string));
dt.Columns.Remove(joinField);
foreach (DataColumn col in dataTable2.Columns)
dt.Columns.Add(col.ColumnName, typeof(string));
foreach (var row in joinTable)
{
var newRow = dt.NewRow();
newRow.ItemArray = row.t1.ItemArray.Union(row.t2.ItemArray).ToArray();
dt.Rows.Add(newRow);
}
return dt;
}
I tried to do this in next way
public static DataTable JoinTwoTables(DataTable innerTable, DataTable outerTable)
{
DataTable resultTable = new DataTable();
var innerTableColumns = new List<string>();
foreach (DataColumn column in innerTable.Columns)
{
innerTableColumns.Add(column.ColumnName);
resultTable.Columns.Add(column.ColumnName);
}
var outerTableColumns = new List<string>();
foreach (DataColumn column in outerTable.Columns)
{
if (!innerTableColumns.Contains(column.ColumnName))
{
outerTableColumns.Add(column.ColumnName);
resultTable.Columns.Add(column.ColumnName);
}
}
for (int i = 0; i < innerTable.Rows.Count; i++)
{
var row = resultTable.NewRow();
innerTableColumns.ForEach(x =>
{
row[x] = innerTable.Rows[i][x];
});
outerTableColumns.ForEach(x =>
{
row[x] = outerTable.Rows[i][x];
});
resultTable.Rows.Add(row);
}
return resultTable;
}
Note that if you have a DataSet, you will need to steal the table from the Dataset with dataSet.Table[0]

Categories

Resources