Compare two DataTables and Display the differences in another datatable in LINQ - c#

I have two DataTables and I want to display the rows. if both the datatables having the same value, Then mark X in all columns or else select the column with highest value(Eg:DT1: 10,DT2 :5)
Datatable1
id Name Weight
1 Ship 500
2 Train 600
3 Plane 700
4 Car 800
Datatable2
id Name Weight
1 Ship 500
3 Plane 600
4 Car 200
I want the result to be:
Datatable3
id Name Weight Datatable1 Datatable2
1 Ship 500 X X
2 Train 600 X
3 Plane 700 X X
4 Car 800 X
I have tried the below:-
DataTable Datatable3 = (from a in Datatable1.AsEnumerable()
join b in Datatable2.AsEnumerable()
on a["Name"].ToString() equals b["Name"].ToString()
a["Weight"].ToString() equals b["Weight"].ToString() into g
where g.Count() != 1 select a).CopyToDataTable();
dataGrid1.ItemsSource = Datatable3.DefaultView;
Please help me on this. Thanks in advance

This is what I have:-
DataTable Datatable3 = dt1.AsEnumerable().Union(dt2.AsEnumerable())
.GroupBy(x => x.Field<int>("Id"))
.Select(x =>
{
var topWeightItem = x.OrderByDescending(z => z.Field<int> ("Weight")).First();
return new Items
{
Id = x.Key,
Name = topWeightItem.Field<string>("Name"),
Weight = topWeightItem.Field<int>("Weight"),
DataTable1 = dt1.AsEnumerable().Any(z => z.Field<int>("Id") == x.Key
&& z.Field<int>("Weight") == topWeightItem.Field<int>("Weight")
&& z.Field<string>("Name") == topWeightItem.Field<string>("Name"))
? "X" : String.Empty,
DataTable2 = dt2.AsEnumerable().Any(z => z.Field<int>("Id") == x.Key
&& z.Field<int>("Weight") == topWeightItem.Field<int>("Weight")
&& z.Field<string>("Name") == topWeightItem.Field<string>("Name"))
? "X" : String.Empty
};
}
).PropertiesToDataTable<Items>();
Since It is returning an anonymous type, you can't use CopyToDataTable method, so please check this to understand how I converted it into a datatable.
I am getting this output:-
I have used following type for conversion purpose:-
public class Items
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Weight { get; set; }
public string DataTable1 { get; set; }
public string DataTable2 { get; set; }
}

I have written in two query to achieve what you said..Perhaps you can optimize it further.
//datatable1
DataTable dt1 = new DataTable();
dt1.Columns.Add("Id");
dt1.Columns.Add("Name");
dt1.Columns.Add("Weight");
DataRow dr ;
dr = dt1.NewRow();
dr["Id"] = 1;
dr["Name"] = "Ship";
dr["Weight"] = 500;
dt1.Rows.Add(dr);
dr = dt1.NewRow();
dr["Id"] = 2;
dr["Name"] = "Train";
dr["Weight"] = 600;
dt1.Rows.Add(dr);
dr = dt1.NewRow();
dr["Id"] = 3;
dr["Name"] = "Plane";
dr["Weight"] = 700;
dt1.Rows.Add(dr);
dr = dt1.NewRow();
dr["Id"] = 4;
dr["Name"] = "Car";
dr["Weight"] = 400;
dt1.Rows.Add(dr);
//datatable2
DataTable dt2 = new DataTable();
dt2.Columns.Add("Id");
dt2.Columns.Add("Name");
dt2.Columns.Add("Weight");
DataRow dr2;
dr2 = dt2.NewRow();
dr2["Id"] = 1;
dr2["Name"] = "Ship";
dr2["Weight"] = 500;
dt2.Rows.Add(dr2);
dr2 = dt2.NewRow();
dr2["Id"] = 3;
dr2["Name"] = "Plane";
dr2["Weight"] = 700;
dt2.Rows.Add(dr2);
dr2 = dt2.NewRow();
dr2["Id"] = 4;
dr2["Name"] = "Car";
dr2["Weight"] = 400;
dt2.Rows.Add(dr2);
//iterate through table1
IEnumerable<DataRow> table1 = from r in dt1.AsEnumerable()
select r;
//iterate through table2
IEnumerable<DataRow> table2 = from r in dt2.AsEnumerable()
select r;
Console.WriteLine("Id\tName\tWeight\tDatatable1\tDatatable2");
Console.WriteLine("----------------------------------------------------");
//prints the common records
foreach (DataRow td1 in table1.Distinct())//Matches wholes of the Element Sequence inside IEnumerable
{
table2.Distinct().ToList().ForEach(td2 =>
{
if (td1.Field<string>("Id") == td2.Field<string>("Id"))
{
Console.WriteLine(td1.Field<string>("Id") + "\t" + td1.Field<string>("Name") + "\t" + td1.Field<string>("Weight") + "\t" + "x" + "\t\t" + "x");
}
});
}
//prints the missing records
var query = (from tb1 in dt1.AsEnumerable()
join tb2 in dt2.AsEnumerable()
on tb1.Field<string>("Id") equals tb2.Field<string>("Id") into subset
from sc in subset.DefaultIfEmpty()
where sc == null
select new
{
id = tb1.Field<string>("Id"),
name = tb1.Field<string>("Name"),
wt = tb1.Field<string>("Weight")
}).Distinct();
foreach (var td1 in query)
{
Console.WriteLine(td1.id + "\t" + td1.name + "\t" + td1.wt + "\t" + "x" + "\t\t" + "-");
}

Related

Stackedcolumn chart has gaps and y value is not starting at 0

I have a data like this in my database:
But when I output this on a stackedcolumn, I get this result and it has gap and it didnt start at 0
Here's my code:
string query = "SELECT * FROM TBL_SAMPLE INNER JOIN TBL_AREA ON TBL_SAMPLE.AREA = TBL_AREA.AREA";
DataTable dt = GetData(query);
//Get the DISTINCT Foods.
List<string> countries = (from p in dt.AsEnumerable()
select p.Field<string>("FOOD")).Distinct().ToList();
//Loop through the Foods.
foreach (string food in countries)
{
//Add Series to the Chart.
Chart1.Series.Add(new Series(food));
Chart1.Series[food].IsValueShownAsLabel = true;
//Get the Area for each Food.
int[] x = (from p in dt.AsEnumerable()
where p.Field<string>("FOOD") == food
select p.Field<int>("AREA_ID")).ToArray();
//Get the Total each food.
int[] y = (from p in dt.AsEnumerable()
where p.Field<string>("FOOD") == food
select p.Field<int>("TOTAL")).ToArray();
Chart1.Series[food].ChartType = SeriesChartType.StackedColumn;
Chart1.Series[food].Points.DataBindXY(x, y);
}
Chart1.Legends[0].Enabled = true;

Calculating percentage of a groups from datatable

I have a datatable that looks like below
My result Should be A=40% , B=60% .. ie 2/5 and 3/5
Group name can be A, B, C, etc...
How can i calculate the figures based on that datatable values??
You could use LINQ and cast it to a dictionary:
DataTable dt = new DataTable("test1");
dt.Columns.AddRange(new DataColumn[] { new DataColumn("TASKID"), new DataColumn("GROUPID"), new DataColumn("GROUPNAME") });
dt.Rows.Add(new object[] { 12, 2, "A" });
dt.Rows.Add(new object[] { 13, 3, "B" });
dt.Rows.Add(new object[] { 12, 2, "A" });
dt.Rows.Add(new object[] { 14, 3, null });
dt.Rows.Add(new object[] { 15, 3, "B" });
var query = (from DataRow row in dt.Rows
group row by row["GROUPNAME"] into g
select g).ToDictionary(x => (x.Key.ToString() == "" ? "*" : x.Key.ToString()), x => (int)((x.Count() * 100) / dt.Rows.Count));
Iterate through the dictionary to display the values:
foreach(KeyValuePair<string,int> kvp in query)
Console.WriteLine(kvp.Key + " - " + kvp.Value.ToString());
The output:
A - 40
B - 40
* - 20
The percentage is cast as an int. simply change (int)((x.Count() * 100) / dt.Rows.Count) if you need more accurate values.
A simple way, through loop. The following should be similar to the one you require.
//Simulated datatable
DataTable table1 = new DataTable();
table1.Columns.Add(new DataColumn("TaskID", typeof(int)));
table1.Columns.Add(new DataColumn("GroupID", typeof(int)));
table1.Columns.Add(new DataColumn("GroupName", typeof(String)));
//Entered test values
DataRow dr1 = null;
dr1 = table1.NewRow();
dr1["TaskID"] = 12;
dr1["GroupID"] = 2;
dr1["GroupName"] = "A";
table1.Rows.Add(dr1);
dr1 = table1.NewRow();
dr1["TaskID"] = 13;
dr1["GroupID"] = 3;
dr1["GroupName"] = "B";
table1.Rows.Add(dr1);
dr1 = table1.NewRow();
dr1["TaskID"] = 14;
dr1["GroupID"] = 2;
dr1["GroupName"] = "A";
table1.Rows.Add(dr1);
dr1 = table1.NewRow();
dr1["TaskID"] = 15;
dr1["GroupID"] = 3;
dr1["GroupName"] = "B";
table1.Rows.Add(dr1);
dr1 = table1.NewRow();
dr1["TaskID"] = 16;
dr1["GroupID"] = 3;
dr1["GroupName"] = "B";
table1.Rows.Add(dr1);
//solution starts from here
Dictionary<string, int> totalCount = new Dictionary<string, int>();
for (int i = 0; i < table1.Rows.Count; i++)
{
if (totalCount.Keys.Contains(table1.Rows[i]["GroupName"].ToString()))
{
int currVal = totalCount[table1.Rows[i]["GroupName"].ToString()];
totalCount[table1.Rows[i]["GroupName"].ToString()] = currVal + 1;
}
else
{
totalCount[table1.Rows[i]["GroupName"].ToString()] = 1;
}
}
foreach (var item in totalCount)
{
MessageBox.Show(item.Value.ToString());
}
OR
//solution starts from here
var data = table1.AsEnumerable().GroupBy(m => m.Field<string>("GroupName")).Select(grp => new
{
GroupName = grp.Key,
Count = (int)grp.Count()
}).ToList();
Hope this helps
Use Linq
var data=datatable.AsEnumerable().GroupBy(m => m.Field<string>("GROUPNAME")).Select(grp => new
{
GROUPNAME= grp.Key,
Count = (int)grp.Count()
});

Calculate sum of each row using LINQ

I have a datatable of below columns
Price1 Price2 Price3 Price4 ColSum
------ ------ ------ ------ ------
2.5 4.7 8 99
10 39 88.3 90
99 21 33 3.2
Now, how do I calculate sum of each row using LINQ?
I want to achieve
ColSum = sum(Price1+Price2+Price3+Price4)
for each columns by linq.
You don't need Linq at all; simply use the Expression property:
Gets or sets the expression used to filter rows, calculate the values in a column, or create an aggregate column.
Example:
var dt = new DataTable();
dt.Columns.Add("Price1", typeof(decimal));
dt.Columns.Add("Price2", typeof(decimal));
dt.Columns.Add("Price3", typeof(decimal));
dt.Columns.Add("Price4", typeof(decimal));
dt.Columns.Add("ColSum", typeof(decimal));
dt.Rows.Add(new object[]{2.5, 4.6, 8, 99});
dt.Rows.Add(new object[]{10, 39, 88.3, 90});
dt.Rows.Add(new object[]{99, 21, 33, 3.2});
dt.Columns["ColSum"].Expression = "Price1+Price2+Price3+Price4";
dt now looks like:
This way, you can keep your DataTable and the ColSum column is automatically updated for you.
You just want the sum of each row in a calculated member?
var result = myTable.AsEnumerable().Select(r =>
new {
Price1 = r.Price1,
Price2 = r.Price2,
Price3 = r.Price3,
Price4 = r.Price4,
ColSum = r.Price1 + r.Price2 + r.Price3 + r.Price4
});
var sums = Prices.Select(i => new { ColSum= i.Price1 + i.Price2 + i.Price3 + i.Price4 });
foreach (var sum in sums)
{
Console.WriteLine(sum.ColSum.ToString());
}
var sum = objectList.Select(i => new
{
Sum = i.Price1 + i.Price2+i.Price3+i.Price4
});
With the data table
var result = from p in dt.AsEnumerable()
select new
{
Sum = p.Field<double>("Price1") + p.Field<double>("Price2") + p.Field<double>("Price3") + p.Field<double>("Price4")
};
Try this
var result = from p in priceList
select new
{
Price1 = p.Price1,
Price2 = p.Price2,
Price3 = p.Price3,
Price4 = p.Price4,
ColSum = p.Price1 + p.Price2 + p.Price3 + p.Price4 + p.Price5
};
You should iterate through columns collection for each row:
int sum;
foreach (DataRow row in dt.Rows)
{
sum = dt.Columns.Cast<DataColumn>().Sum(column => Convert.ToInt32(row[column]));
MessageBox.Show("Sum for current row: " + sum);
}

Merging two datatable in memory and grouping them to get sum of columns.Using linq but kind of lost here

I have two table where two column are fixed. Some columns are identical and some are new.Columns are dynamic.
Have to do it in code level and I am trying to loop and conditions
What I want is to generate a report following the condition,
All columns in table1 and table2 must be present.
If a column is common and value is there it should be added with the identical row in other table.
If any row is present in one table but not in other, it should be included.
Example data
Table1
ID | NAME | P1 | P2 | P3
----------------------------
1 | A1 | 1 | 2 | 3.3
2 | A2 | 4.4 | 5 | 6
TABLE 2
ID | NAME | P1 | P2 | P4
---------------------------
1 | A1 | 10 | 11 | 12
2 | A2 | 12 | 14 | 15
3 | A3 | 16 | 17 | 18
Expected output:
ID | NAME | P1 | P2 | P3 | P4
---------------------------------
1 | A1 | 11 | 13 | 3.3 | 12
2 | A2 | 16.4 | 19 | 6 | 15
3 | A3 | 16 | 17 | null| 18
Progress till now:
First I merged those two table in to table1
table1.Merge(table2)
Then trying to group by over it
var query = from row in table1.AsEnumerable()
group row by new
{
ID = row.Field<int>("ID"),
Name = row.Field<string>("Name")
}
into grp
select new
{
ID = grp.Key.ID,
Name = grp.Key.Name,
Phase1 = grp.Sum(r => r.Field<decimal>("P1"))
};
I have modified this code to get a datatable. Please see attached cs file.
This is working, but as the number of columns are dynamic, I guess I have to repeat it for other columns and join all these small tables where one columns will be added.
How can I merge all those small tables?
I am lost here.Is there any other way. Its feeling as stupid thing.
Any help would be appreciated.
Attached File:
http://dl.dropbox.com/u/26252340/Program.cs
You want to use an implementation of a full outer join. Something like what follows.
Some setup so you can try this yourself:
DataTable t1 = new DataTable();
t1.Columns.Add("ID", typeof(int));
t1.Columns.Add("Name", typeof(string));
t1.Columns.Add("P1", typeof(double));
t1.Columns.Add("P2", typeof(double));
t1.Columns.Add("P3", typeof(double));
DataRow dr1 = t1.NewRow();
dr1["ID"] = 1;
dr1["Name"] = "A1";
dr1["P1"] = 1;
dr1["P2"] = 2;
dr1["P3"] = 3.3;
t1.Rows.Add(dr1);
DataRow dr2 = t1.NewRow();
dr2["ID"] = 2;
dr2["Name"] = "A2";
dr2["P1"] = 4.4;
dr2["P2"] = 5;
dr2["P3"] = 6;
t1.Rows.Add(dr2);
DataTable t2 = new DataTable();
t2.Columns.Add("ID", typeof(int));
t2.Columns.Add("Name", typeof(string));
t2.Columns.Add("P1", typeof(double));
t2.Columns.Add("P2", typeof(double));
t2.Columns.Add("P4", typeof(double));
DataRow dr3 = t2.NewRow();
dr3["ID"] = 1;
dr3["Name"] = "A1";
dr3["P1"] = 10;
dr3["P2"] = 11;
dr3["P4"] = 12;
t2.Rows.Add(dr3);
DataRow dr4 = t2.NewRow();
dr4["ID"] = 2;
dr4["Name"] = "A2";
dr4["P1"] = 12;
dr4["P2"] = 14;
dr4["P4"] = 15;
t2.Rows.Add(dr4);
DataRow dr5 = t2.NewRow();
dr5["ID"] = 3;
dr5["Name"] = "A3";
dr5["P1"] = 16;
dr5["P2"] = 17;
dr5["P4"] = 18;
t2.Rows.Add(dr5);
The queries look like:
var ids = (from r1 in t1.AsEnumerable() select new { ID = r1["ID"], Name = r1["Name"] }).Union(
from r2 in t2.AsEnumerable() select new { ID = r2["ID"], Name = r2["Name"] });
var query = from id in ids
join r1 in t1.AsEnumerable() on id equals new { ID = r1["ID"], Name = r1["Name"] } into left
from r1 in left.DefaultIfEmpty()
join r2 in t2.AsEnumerable() on id equals new { ID = r2["ID"], Name = r2["Name"] } into right
from r2 in right.DefaultIfEmpty()
select new
{
ID = (r1 == null) ? r2["ID"] : r1["ID"],
Name = (r1 == null) ? r2["Name"] : r1["Name"],
P1 = (r1 == null) ? r2["P1"] : (r2["P1"] == null) ? r1["P1"] : (double)r1["P1"] + (double)r2["P1"],
P2 = (r1 == null) ? r2["P2"] : (r2["P2"] == null) ? r1["P2"] : (double)r1["P2"] + (double)r2["P2"],
P3 = (r1 == null) ? null : r1["P3"],
P4 = (r2 == null) ? null : r2["P4"]
};
Got this solved by
table1.Merge(table2, true, MissingSchemaAction.Add);
finalTable = table1.Clone();
finalTable.PrimaryKey = new DataColumn[] { finalTable.Columns["ID"], finalTable.Columns["Name"] };
List<string> columnNames = new List<string>();
for (int colIndex = 2; colIndex < finalTable.Columns.Count; colIndex++)
{
columnNames.Add(finalTable.Columns[colIndex].ColumnName);
}
foreach (string cols in columnNames)
{
var temTable = new DataTable();
temTable.Columns.Add("ID", typeof(int));
temTable.Columns.Add("Name", typeof(string));
temTable.Columns.Add(cols, typeof(decimal));
(from row in table1.AsEnumerable()
group row by new { ID = row.Field<int>("ID"), Team = row.Field<string>("Team") } into grp
orderby grp.Key.ID
select new
{
ID = grp.Key.ID,
Name = grp.Key.Team,
cols = grp.Sum(r => r.Field<decimal?>(cols)),
})
.Aggregate(temTable, (dt, r) => { dt.Rows.Add(r.ID, r.Team, r.cols); return dt; });
finalTable.Merge(temTable, false, MissingSchemaAction.Ignore);
}
Since the columns are dynamic you'll need to return an object with dynamic properties. You could do this with an ExpandoObject.
The following code is ugly in many ways - I would do some massive refactoring before letting it go - but it gets the job done and might help you out to achieve what you want.
(Sorry for using the other linq syntax.)
var query = table1.AsEnumerable()
.GroupBy(row => new
{
ID = row.Field<int>("ID"),
Name = row.Field<string>("Name")
})
.Select(grp =>
{
dynamic result = new ExpandoObject();
var dict = result as IDictionary<string, object>;
result.ID = grp.Key.ID;
result.Name = grp.Key.Name;
foreach (DataRow row in grp)
{
foreach (DataColumn column in table1.Columns)
{
string columnName = column.ColumnName;
if (columnName.Equals("ID") || columnName.Equals("Name"))
continue;
//else
if (!dict.Keys.Contains(columnName))
dict[columnName] = row[columnName];
else
{
if (row[columnName] is System.DBNull)
continue;
if (dict[columnName] is System.DBNull)
{
dict[columnName] = row[columnName];
continue;
}
//else
dict[columnName] = (decimal)dict[columnName] + (decimal)row[columnName];
}
}
}
return result;
});

LINQ and C# summary

I have a relational table which has parent child rows. Some of the rows have a typeId and I would like to get a summary of the total cost by the type:
The list on the left is some sample data and the list on the right is the summary result expected.
The data is in a datatable. Can anyone help me with this.
ie:
typeId = 1: Which would be calculated by: 30 (total cost of 3,2) x 5 (qty of 2,1) x 3 (qty of 1,null)
(30 x 5 x 3) = 450
typeId = 2: Which would be calculated by: 12 (total cost of 5,1) x 3 (qty of 1,null)
x 15 (total cost 4,2) x 5 (qty of 2,1) x 3 (qty of 1,null)
(12 x 3) + (15 x 5 x 3) = 261
Here is some sample code
DataTable dt = new DataTable( "Summary" );
dt.Columns.Add( "Id", Type.GetType( "System.Int32" ) );
dt.Columns.Add( "ParentId", Type.GetType( "System.Int32" ) );
dt.Columns.Add( "Qty", Type.GetType( "System.Int32" ) );
dt.Columns.Add( "Cost", Type.GetType( "System.Decimal" ) );
dt.Columns.Add( "TotalCost", Type.GetType( "System.Decimal" ) );
dt.Columns.Add( "TypeId", Type.GetType( "System.Int32" ) );
dt.Rows.Clear();
DataRow row = dt.NewRow();
row["Id"] = 1;
row["ParentId"] = DBNull.Value;
row["Qty"] = 3;
row["Cost"] = 237.00;
row["TotalCost"] = 711.00;
row["TypeId"] = DBNull.Value;
dt.Rows.Add( row );
row = dt.NewRow();
row["Id"] = 2;
row["ParentId"] = 1;
row["Qty"] = 5;
row["Cost"] = 45.00;
row["TotalCost"] = 225.00;
row["TypeId"] = DBNull.Value;
dt.Rows.Add( row );
row = dt.NewRow();
row["Id"] = 3;
row["ParentId"] = 2;
row["Qty"] = 30;
row["Cost"] = 1.00;
row["TotalCost"] = 30.00;
row["TypeId"] = 1;
dt.Rows.Add( row );
row = dt.NewRow();
row["Id"] = 4;
row["ParentId"] = 2;
row["Qty"] = 1;
row["Cost"] = 15.00;
row["TotalCost"] = 15.00;
row["TypeId"] = 2;
dt.Rows.Add( row );
row = dt.NewRow();
row["Id"] = 5;
row["ParentId"] = 1;
row["Qty"] = 4;
row["Cost"] = 3.00;
row["TotalCost"] = 12.00;
row["TypeId"] = 2;
dt.Rows.Add( row );
If I understand you correctly, you need a recursive routine which is not something I would try in pure Linq, so I would try something like this.
var summary =
(from r in dt.AsEnumerable()
where r["TypeID"] != DBNull.Value
group r by (int) r["TypeId"] into results
select new
{
results.Key ,
TotalCost = results.Sum(r=> (decimal) r["TotalCost"] * GetParentsQty(r) )
}
);
public int GetParentsQty(DataRow child )
{
if (child["ParentID"] == DBNull.Value)
return 1;
var parent = (from row in dt.AsEnumerable()
where (int) child["ParentID"] == (int) row["Id"]
select row
).Single();
return (int) parent ["Qty"] * GetParentsQty(parent);
}

Categories