LINQ implement multiple select where condition - c#

I have 2 datatables.
DataTable wdt = new DataTable();
wdt.Columns.Add("wName", typeof(string));
wdt.Columns.Add("userID1", typeof(string));
wdt.Columns.Add("userID2", typeof(string));
wdt.Columns.Add("userID3", typeof(string));
wdt.Columns.Add("dttime", typeof(DateTime));
DataTable mdt = new DataTable();
mdt.Columns.Add("iD", typeof(string));
mdt.Columns.Add("firstname", typeof(string));
mdt.Columns.Add("lastname", typeof(string));
DataTable dt = new DataTable();
dt.Columns.Add("wName", typeof(string));
dt.Columns.Add("user1", typeof(string));
dt.Columns.Add("user2", typeof(string));
dt.Columns.Add("user3", typeof(string));
dt.Columns.Add("dttime", typeof(DateTime));
for (int i = 0; i < wdt.Rows.Count; i++)
{
DataRow ndr = dt.NewRow();
ndr[0] = wdt.Select()[i][0].ToString();
ndr[1] = (from r in mdt.AsEnumerable()
where r.Field<int>("iD") == Convert.ToInt32(wdt.Rows[i][1])
select r.Field<string>("firstName") + " " + r.Field<string>("lastName")).First<string>();
ndr[2] = (from r in mdt.AsEnumerable()
where r.Field<int>("iD") == Convert.ToInt32(wdt.Rows[i][2])
select r.Field<string>("firstName") + " " + r.Field<string>("lastName")).First<string>();
ndr[3] = (from r in mdt.AsEnumerable()
where r.Field<int>("iD") == Convert.ToInt32(wdt.Rows[i][3])
select r.Field<string>("firstName") + " " + r.Field<string>("lastName")).First<string>();
ndr[4] = wdt.Select()[i][4].ToString();
dt.Rows.Add(ndr);
}
In the above code, i get a new datatable dt from calculating the data from wdt & mdt. But here, i have to run the LINQ syntaxes in a loop. Is it possible to avoid loop & the same work to be done in a single LINQ loop?
Datatable1 :
iD firstname lastname
1 b v
2 d c
3 f g
4 s o
....
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
Expected 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
Here, iD from datatable1 & userID1, userID2, userID3 are same.

username1 you can Make a query using left join and return new on the fly object witch have all property you need as below
var newData = (from a in wdt.AsEnumerable()
join user1Info in mdt.AsEnumerable() on a["userID1"] equals user1Info["iD"] into lUser1Info
join user2Info in mdt.AsEnumerable() on a["userID2"] equals user2Info["iD"] into lUser2Info
join user3Info in mdt.AsEnumerable() on a["userID3"] equals user3Info["iD"] into lUser3Info
from user1Info in lUser1Info.DefaultIfEmpty()
from user2Info in lUser2Info.DefaultIfEmpty()
from user3Info in lUser3Info.DefaultIfEmpty()
select new
{
wName = a["wName"].ToString(),
username1 = user1Info == null ? string.Empty : user1Info["firstname"].ToString() + user1Info["lastname"],
username2 = user2Info == null ? string.Empty : user2Info["firstname"].ToString() + user2Info["lastname"],
username3 = user3Info == null ? string.Empty : user3Info["firstname"].ToString() + user3Info["lastname"],
dttime = a["dttime"]
}).ToList();

Related

Group and Sum DataTable

I have Data Table with the following data
Number Type Order count
1 1 R 1
1 1 R 1
1 1 R 1
1 2 R 1
I am looking to get to this result
Number Type Order count
1 1 R 3
1 2 R 1
How can I group by three columns
var result = dt.AsEnumerable()
.GroupBy(x => {x.Field<string>("Number"))//need to group by Type and order also need to sum te total counts
rgoal
Your question made me curious, so I did some digging on Stack Overflow.
esc's answer appears will also solve your issue. It is posted under: How do I use SELECT GROUP BY in DataTable.Select(Expression)?:
Applying his method to your problem gave me this solution:
DataTable dt2 = dt.AsEnumerable()
.GroupBy(r => new { Number = r["Number"], Type = r["Type"], Order = r["Order"] })
.Select(g =>
{
var row = dt.NewRow();
row["Number"] = g.Key.Number;
row["Type"] = g.Key.Type;
row["Order"] = g.Key.Order;
row["Count"] = g.Count();
return row;
}).CopyToDataTable();
This will return a DataTable matching the schema of the input DataTable with the grouping and counts you requested.
Here is the full code I use to verify in LINQPad:
DataTable dt = new DataTable("Demo");
dt.Columns.AddRange
(
new DataColumn[]
{
new DataColumn ( "Number", typeof ( int ) ),
new DataColumn ( "Type", typeof ( int ) ),
new DataColumn ( "Order", typeof ( string ) ),
new DataColumn ( "Count", typeof ( int ) )
}
);
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,1,"R", 1 });
dt.Rows.Add(new object[] { 1,2,"R", 1 });
DataTable dt2 = dt.AsEnumerable()
.GroupBy(r => new { Number = r["Number"], Type = r["Type"], Order = r["Order"] })
.Select(g =>
{
var row = dt.NewRow();
row["Number"] = g.Key.Number;
row["Type"] = g.Key.Type;
row["Order"] = g.Key.Order;
row["Count"] = g.Count();
return row;
}).CopyToDataTable();
foreach (DataRow row in dt2.Rows)
{
for (int i = 0; i < dt2.Columns.Count; i++)
Console.Write("{0}{1}",
row[i], // Print column data
(i < dt2.Columns.Count - 1)? " " : Environment.NewLine); // Print column or row separator
}
Here are the results:
1 1 R 3
1 2 R 1

Joining two DataTables in C#

I have the following two data tables (DataTable) that represents PC allocations to students for different timeslots:
Table1:
Timeslot PC1 PC2 PC3
1 A B
2 A B
3 D
4 D
Table2:
Timeslot PC1 PC2 PC3
1 C
2 C
3
4
Would it be possible to join these two DataTables together into one DataTable, as follows?
Wanted:
Timeslot PC1 PC2 PC3
1 A C B
2 A C B
3 D
4 D
I know that, in SQL, it would be quite straight forward left join, but I have no clue how I can do this in C# with DataTables.
A bit shorter and more efficient if the result is merged in one of the source tables:
Table1.Rows.Cast<DataRow>().Join(Table2.Rows.Cast<DataRow>(), a => a[0], b => b[0],
(a, b) => {a[1] += "" + b[1]; a[2] += "" + b[2]; a[3] += "" + b[3]; return a; }).Count();
Here it is using LINQ:
var joinData = t1.Select().Join(t2.Select(), j1 => j1["TimeSlot"], j2 => j2["TimeSlot"],
(j1, j2) => new { j1, j2 }).Select(j => new
{
TimeSlot = j.j1["TimeSlot"],
PC1 = (string)j.j1["PC1"] != "" ? j.j1["PC1"] : j.j2["PC1"],
PC2 = (string)j.j1["PC2"] != "" ? j.j1["PC2"] : j.j2["PC2"],
PC3 = (string)j.j1["PC3"] != "" ? j.j1["PC3"] : j.j2["PC3"]
});
var resultTable = new DataTable();
addColumns(resultTable);
foreach(var row in joinData)
{
var dr = resultTable.NewRow().ItemArray = new object[] { row.TimeSlot, row.PC1, row.PC2, row.PC3 };
resultTable.Rows.Add(dr);
WriteLine($"{dr[0]} {dr[1]} {dr[2]} {dr[3]}");
}

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

Compare two DataTables and Display the differences in another datatable in LINQ

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

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

Categories