Add up values from two datatables into a new datatable - c#

I have two strongly typed Datatable (dt1):
|FirstName|LastName|Val1|Val2|
|Tony |Stark |34 |35 |
|Steve |Rogers |12 |23 |
|Natasha |Romanoff|2 |100 |
and the second (dt2)
|FirstName|LastName|Val1|Val2|
|Tony |Stark |16 |5 |
|Bruce |Banner |2 |1 |
|Steve |Rogers |54 |40 |
I try to create a new Datatable where I add up the values for the persons. I need a outer join since I need all persons and the value in the second table is halved.
So the result should looks like:
|FirstName|LastName|Val1|Val2|
|Tony |Stark |42 |37.5|
|Steve |Rogers |39 |43 |
|Natasha |Romanoff|2 |100 |
|Bruce |Banner |1 |0.5 |
My approach was with LINQ:
Dim query =
from a in ds1.Table1
Join b in ds2.Table2
On a.FirstName + a.LastName Equals b.FirstName + b.Lastname
Select New With {
.FirstName = a.FirstName,
.LastName = a.LastName,
.Val1 = a.Val1 + b.Val1 *0.5,
.Val2 = a.Val2 + b.Val2 *0.5
}
But I dont get all persons with the approach. I also tried
Dim query =
From a in ds1.Table1
From b in ds2.Table2
Select New With{
Key .KeyName = a.FirstName + a.LastName = b.FirstName + b.FirstName,
.Val1 = a.Val1 + b.Val1 *0.5,
.Val2 = a.Val2 + b.Val2 * 0.5
}
Now I get many entries for each person. Could anyone help me get this done. I dont know if there is maybe another approach without Linq to solve this.

An example using group by. The thing to consider is that before performing the group by and the Sum operation all the values in the second table must be divided by 2. I take care of that before the .Concat
var dt1 = new List<MyClass>();
dt1.Add(new MyClass { FirstName = "Tony", LastName = "Stark", Val1 = 34, Val2 = 35});
dt1.Add(new MyClass { FirstName = "Steve", LastName = "Rogers", Val1 = 12, Val2 = 23});
dt1.Add(new MyClass { FirstName = "Natasha", LastName = "Romanoff", Val1 = 2, Val2 = 100 });
var dt2 = new List<MyClass>();
dt2.Add(new MyClass { FirstName = "Tony", LastName = "Stark", Val1 = 16, Val2 = 5 });
dt2.Add(new MyClass { FirstName = "Bruce", LastName = "Banner", Val1 = 2, Val2 = 1 });
dt2.Add(new MyClass { FirstName = "Steve", LastName = "Rogers", Val1 = 54, Val2 = 40 });
var q = from a in dt1
.Concat(
from b in dt2
select new MyClass
{
FirstName = b.FirstName,
LastName = b.LastName,
Val1 = b.Val1 * 0.5m,
Val2 = b.Val2 * 0.5m
})
group a by new {a.FirstName, a.LastName}
into g
select new
{
g.First().FirstName,
g.First().LastName,
Val1 = g.Sum(x => x.Val1),
Val2 = g.Sum(x => x.Val2),
};
foreach (var s in q)
{
Console.WriteLine("{0} {1} {2} {3}", s.FirstName,s.LastName,s.Val1,s.Val2);
}
Result
Tony Stark 42,0 37,5
Steve Rogers 39,0 43,0
Natasha Romanoff 2 100
Bruce Banner 1,0 0,5

I got it now. My Solution is:
Dim qLeft = From a in dt1.Getdt1Info
Group Join b in dt2.Getdt2Info
On a.FirstName + a.LastName Equals b.FirstName + b.LastName
Into NewGroup = Group
From c in NewGroup.DefaultIfEmpty
Select New With{
.Name = a.FirstName + ", " + a.LastName,
.Value1 = a.Val1 + If (c is nothing, 0D, c.Val1) * 0.5,
.Value2 = a.Val2 + If (c is nothing, 0D, c.Val2) * 0.5
}
Dim qRight =From a in dt2.Getdt2Info
Group Join b in dt1.Getdt1Info
On a.FirstName + a.LastName Equals b.FirstName + b.LastName
Into NewGroup = Group
From c in NewGroup.DefaultIfEmpty
Select New With{
.Name = a.FirstName + ", " + a.LastName,
.Value1 = a.val1 * 0.5 + If (c Is Nothing, 0D, c.Val1),
.Value2 = a.val2 * 0.5 + If (c Is Nothing, 0d, c.Val2)}
Dim qFull = qleft.Concat(qRight).GroupBy(function(x) x.Name).Select(function(x) x.First)
I still dont know why qleft.Union(qRight) doesnt eliminate the duplicates. I solved that with the functions (s. Code).

Related

How to combine many rows into single text?

I want to grouping phone number to the same section like this :
section | phone
1 | 1001
1 | 1002
2 | 1201
2 | 1202
and grouping them in like this :
section | phone
1 | 1001, 1002
2 | 1201, 1202
but i don't know syntax to groping them to that.
This code i do
var entgr = (from fx in MainOnline.MA_TelUsers
where fx.TE_SectionID != null
group fx by fx.TE_SectionID into id
from ids in id.DefaultIfEmpty()
select new
{
Section = ids.TE_SectionID,
TelPhone = ids.TE_Phone
});
How I grouping that and use it to join other table?
var entgr = (from fx in ph
group fx by fx.SectionId.ToString() into id
select new
{
Section = id.Key,
TelPhone = string.Join(", ",id.Select(s => s.PhoneNumber.ToString()))
});
try this query
var entgr = (from fx in MainOnline.MA_TelUsers
where fx.TE_SectionID != null
group fx by fx.TE_SectionID into ids
select new
{
Section = ids.TE_SectionID,
TelPhone =ids.Aggregate((a, b) =>
new {TelPhone = (a.TelPhone + ", " + b.TelPhone ) }).TelPhone
});
https://msdn.microsoft.com/en-us/library/vstudio/bb397696.aspx
See this link. If you want to it do it in single linq query then that I hope not possible.
But at evaluation time you can do like this
var ph = new List<Phone>();
ph.Add(new Phone() { SectionId = 1, PhoneNumber = 1001 });
ph.Add(new Phone() { SectionId = 1, PhoneNumber = 1002 });
ph.Add(new Phone() { SectionId = 2, PhoneNumber = 1201 });
ph.Add(new Phone() { SectionId = 2, PhoneNumber = 1202 });
var results = ph.GroupBy(i => i.SectionId).Select(i=> new {i.Key, i});
foreach (var phone in results)
{
int section = phone.Key;
string phoneNos = string.Join(",",phone.i.Select(i=>i.PhoneNumber));
}

Join DataTables to get new DataTable via LINQ

I've a datatable named which contains data with a column of ID, firstname, lastname. Another datatable contains columns of code, userID1, userID2, userID3, work.
Now i want a new datatable which should contain the column of both the datatable with proper data.
New datatable should contain data as: ID, userfullname1, userfullname2, userfullname3, work.
Here we get the value of userfullname1 by firstname, lastname of datatable1 & userID1 of datatable2. Similarly we get the value of userfullname2 by firstname, lastname of datatable1 & userID2 of datatable2 & so on.
The value of ID in Datatable1 is same as userID1, userID2, userID3 in Datatable2.
Finally, i want to obtain a new datatable with code, userfullname1, userfullname2, userfullname3, work. But users IDs are in datatable1. So, i want to bind the names of Datatable1 to the all 3 userids of Datatable2 via their IDs whichare present in both the tables.
Datatable1 :
iD name
1 b
2 d
3 f
4 s
....
Datatable2 :
Code userid1 userid2 userid3 work
1f 1 3 6 gg
2g 1 4 7 gg
3b 3 4 7 gg
4v 4 3 8 gg
New Datatable :
Code username1 username2 username3 work
1f a b c gg
2g d f r gg
3b c h g gg
4v d s h gg
How can i join & get the new datatable ?
It sounds like you have a table with User Ids and you want to join your profile table to get those names. You can do this with sub-queries like:
var myTable = UserIds.Select(u => new
{
User1FullName = u.UserProfiles.FirstOrDefault(p => p.UserId == u.userId1).Select(p => p.FirstName + " " + p.LastName),
User2FullName = u.UserProfiles.FirstOrDefault(p => p.UserId == u.userId2).Select(p => p.FirstName + " " + p.LastName)
// etc...
});
You can do it with joins like:
var myTable = (from u in UserIds
join p1 in UserProfiles on u.UserId1 equals p1.UserId
join p2 in UserProfiles on u.UserId2 equals p2.UserId
// etc...
select new
{
User1FullName = p1.FirstName + " " + p1.LastName,
User2FullName = p2.FirstName + " " + p2.LastName,
// etc...
});
You can use LINQ to achieve what you want:
DataTable tblResult = new DataTable();
tblResult.Columns.Add("ID");
tblResult.Columns.Add("userfullname1");
tblResult.Columns.Add("userfullname2");
tblResult.Columns.Add("userfullname3");
tblResult.Columns.Add("Work");
var query = from r1 in datatable1.AsEnumerable()
from r2 in datatable2.AsEnumerable()
let id = r1.Field<int>("ID")
let userID1 = r2.Field<int>("userID1")
let userID2 = r2.Field<int>("userID2")
let userID3 = r2.Field<int>("userID3")
where id == userID1 && id == userID2 && id == userID3
select new { r1, r2, id, userID1, userID2, userID3 };
foreach (var x in query)
{
DataRow row = tblResult.Rows.Add();
string firstName = x.r1.Field<string>("firstname");
string lastName = x.r1.Field<string>("lastname");
string userfullname1 = string.Format("{0} {1} {2}", firstName, lastName, x.userID1);
string userfullname2 = string.Format("{0} {1} {2}", firstName, lastName, x.userID2);
string userfullname3 = string.Format("{0} {1} {2}", firstName, lastName, x.userID3);
row.SetField("ID", x.id);
row.SetField("userfullname1", userfullname1);
row.SetField("userfullname2", userfullname2);
row.SetField("userfullname3", userfullname3);
row.SetField("Work", x.r2.Field<string>("Work"));
}
If you just want your resulting table to have name and id, you should do it like this:
DataTable dtResult = new DataTable();
dtResult.Columns.Add("ID", typeof(string));
dtResult.Columns.Add("name", typeof(string));
var result = from datatable1 in table1.AsEnumerable()
join datatable2 in table2.AsEnumerable()
on datatable1.Field<string>("ID") equals datatable2.Field<string>("userID")
select dtResult.LoadDataRow(new object[]
{
datatable1.Field<string>("ID"),
string.Format("{0} {1}" ,datatable1.Field<string>("fname") ,datatable1.Field<string>("lname")),
}, false);
result.CopyToDataTable();

Group the result using linq

I have some problems and don't know what to do, can someone help me please?
I have a table like that:
ID Name Produkt Comment aa bb
1 Mike AA YY x
1 Mike AA YY x
I want to group with linq the result like to have Mike but just in one line
1 Mike AA YY x x
My code:
var results = from t1 in table.AsEnumerable()
join tb2 in table2.AsEnumerable()
on t1.Field<string>("Name") equals tb2.Field<string>("Name") into prodGroup
from table4 in prodGroup.DefaultIfEmpty()
select new
{
ID = t1.Field<Int32?>("ID"),
Name = t1.Field<String>("Name"),
Produkt = t1.Field<String>("Produkt"),
Attribute = t1.Field<String>("Attribute"),
Comment = table4 != null ? table4.Field<String>("Comment") : null,
};
foreach (var r in results)
{
var productIndex = result.Columns.IndexOf(r.Attribute);
var vals = new List<object>() { r.ID, r.Name, r.Produkt, r.Comment };
for (int i = 4; i < result.Columns.Count; i++)
{
if (i == productIndex)
{
vals.Add(true);
}
else
{
vals.Add(false);
}
}
result.LoadDataRow(vals.ToArray(), true);
}
Try this:
var result = from t1 in table.AsEnumerable()
group t1 by t1.Name into t1group
select new
{
Name = t1group.FirstOrDefault().Name
,ID = t1group.FirstOrDefault().ID
,Product = t1group.FirstOrDefault().Product
,Comment = t1group.FirstOrDefault().Comment
,aa = (t1group.Select(x => x.aa??"" + x.bb??"")).Aggregate((a, b) => (a + ", " + b))
};

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;
});

c # using linq to group by multiple columns in a datatable

I have three columns in a datatable: string, DateTime, and decimal. I want to group by the string and decimal column, and for the rows grouped I want to sum the decimal values. I know how to do the sum part, but how do you group two different columns in a datatable?
This is my code so far which doesn't work properly:
var newSort = from row in objectTable.AsEnumerable()
group row by new {ID = row.Field<string>("resource_name"), time1 = row.Field<DateTime>("day_date")} into grp
orderby grp.Key
select new
{
resource_name1 = grp.Key.ID,
day_date1 = grp.Key.time1,
Sum = grp.Sum(r => r.Field<Decimal>("actual_hrs"))
};
I don't think you're giving us the full story. Other than orderby not working with anonymous types (the code you gave wouldn't have compiled), your query should work the way you want. I just put this in LINQPad:
var objectTable = new DataTable();
objectTable.Columns.Add("resource_name",typeof(string));
objectTable.Columns.Add("day_date",typeof(DateTime));
objectTable.Columns.Add("actual_hrs",typeof(decimal));
objectTable.Rows.Add(1, DateTime.Today, 1);
objectTable.Rows.Add(2, DateTime.Today, 2);
var newSort = from row in objectTable.AsEnumerable()
group row by new {ID = row.Field<string>("resource_name"), time1 = row.Field<DateTime>("day_date")} into grp
select new
{
resource_name1 = grp.Key.ID,
day_date1 = grp.Key.time1,
Sum = grp.Sum(r => r.Field<Decimal>("actual_hrs"))
};
newSort.Dump();
... and I got these results:
resource_name1 | day_date1 | Sum
1 | 7/1/2011 12:00:00 AM | 1
2 | 7/1/2011 12:00:00 AM | 2
use this code
var newSort = from row in objectTable.AsEnumerable()
group row by new {ID = row.Field<string>("resource_name"), time1 = row.Field<DateTime>("day_date")} into grp
orderby grp.Key
select new
{
resource_name1 = grp.Key.ID,
day_date1 = grp.Key.time1,
Sum = grp.Sum(r => Convert.ToDecimal(r.ItemArray[2]))
};
For those that want a solution in Vb.net, here is an example:
Dim workTable As DataTable = New DataTable("Customers")
Dim workCol As DataColumn = workTable.Columns.Add("ID", Type.GetType("System.Int32"))
workTable.Columns.Add("Total", Type.GetType("System.Decimal"))
workTable.Columns.Add("Compra", Type.GetType("System.Decimal"))
Dim row As DataRow = workTable.NewRow()
row("id") = 2
row("total") = 1.5
row("compra") = 3
workTable.Rows.Add(row)
row = workTable.NewRow()
row("id") = 1
row("total") = 1.5
row("compra") = 3.3999999999999999
workTable.Rows.Add(row)
row = workTable.NewRow()
row("id") = 1
row("total") = 1.5
row("compra") = 5
workTable.Rows.Add(row)
Dim detalles As IEnumerable(Of DataRow) = workTable.AsEnumerable()
Dim query = From detalle In detalles.AsEnumerable() _
Group detalle By grupoClave = New With _
{ _
Key .C2 = detalle("id"), _
Key .C4 = detalle("total")} Into g = Group _
Select New With _
{ _
.Col2 = g(0).Field(Of Integer)("id"), _
.Col3 = g(0).Field(Of Decimal)("total"), _
.Col4 = g.Sum(Function(fact) fact.Field(Of Decimal)("compra")) _
}
For Each p In query
Console.WriteLine((p.Col2 & p.Col3 & p.Col4))
Next

Categories