Left Outer Join with multiple Data tables - c#

I have 3 DataTables
DataTable1
Id Version URL Owner
1 1 "xx" "alice"
2 1 "yy" "bob"
3 1 "zz" "Mike"
4 1 "ww" "Rob"
5 1 "ww" "Bick"
DataTable2
Id Version DomainID Region Type
1 1 aa asia 1
2 1 bb europe 2
3 1 cc africa 1
4 1 dd aus1 0
DataTable3
Id Size FreeSpace
aa 2500 2000
bb 3300 3000
cc 5500 50
Expected Join
Id Version URL Owner DomainID Region Type Size Freespace
1 1 "xx" "alice" aa asia 1 2500 2000
2 1 "yy" "bob" bb europe 2 3300 3000
3 1 "zz" "Mike" cc africa 1 5500 50
4 1 "ww" "sean" dd aus1 0 null null
5 1 "ww" "Bick" null null null null null
I am doing a Join Operation on these tables using Linq as follows:
// Datatable1 joins with Datatable2 on Id and version (datatable1) --> Id and version (datatable2)
// Datatable2 joins with Datatable3 on DomainId(datatable2) --> Id(datatable3)
var result = from dataRows1 in DataTable1.AsEnumerable()
join dataRows2 in DataTable2.AsEnumerable() on
new
{
Id = dataRows1.Field<long>("Id"),
Version = dataRows1.Field<long>("version")
} equals new
{
Id = dataRows2.Field<long>("Id"),
Version = dataRows2.Field<long>("version")
}
into tempJoin
from datarowc in tempJoin.DefaultIfEmpty()
join dataRows3 in DataTable3.AsEnumerable() on
dataRowsc.Field<long>("DomainId") equals dataRows3.Field<long>("Id")
select new
{
datarow1,
datarowc,
datarow3
}
I am getting an exception of datarowc to be null.
Not quite sure why datarowc is null here and how to achieve the expected join.

using System.Data;
using System.Linq;
namespace CodeWars
{
class Program
{
static void Main(string[] args)
{
var result = datarows1.AsEnumerable()
.Select(x => new
{
Tab1Row = x,
Tab2Row = datarows2.AsEnumerable().FirstOrDefault(
y => x.Field<int>("Id") == y.Field<int>("Id") &&
x.Field<int>("Version") == y.Field<int>("Version")
)
}
)
.Select(x => new
{
Tab1Row = x.Tab1Row,
Tab2Row = x.Tab2Row,
Tab3Row = datarows3.AsEnumerable().FirstOrDefault(
y => x?.Tab2Row?.Field<string>("DomainId") == y.Field<string>("Id")
)
}
);
}
static DataTable datarows1 = new DataTable
{
Columns = {
{ "Id", typeof(int) },
{ "Version", typeof(int) },
{ "URL", typeof(string) },
{ "Owner", typeof(string) },
},
Rows = {
{ 1, 1, "xx", "alice" },
{ 2, 1, "yy", "bob" },
{ 3, 1, "vv", "mike" },
{ 4, 1, "ww", "rob" },
{ 5, 1, "zz", "bick" },
}
};
static DataTable datarows2 = new DataTable
{
Columns = {
{ "Id", typeof(int) },
{ "Version", typeof(int) },
{ "DomainID", typeof(string) },
{ "Region", typeof(string) },
{ "Type", typeof(int) },
},
Rows = {
{ 1, 1, "aa", "asia", 1 },
{ 2, 1, "bb", "europe", 2},
{ 3, 1, "cc", "asia", 1},
{ 4, 1, "dd", "aus1", 0},
}
};
static DataTable datarows3 = new DataTable
{
Columns = {
{ "Id", typeof(string) },
{ "Size", typeof(int) },
{ "FreeSpace", typeof(int) },
},
Rows = {
{ "aa", 2500, 2000 },
{ "bb", 3300, 3000 },
{ "cc",5500, 50},
}
};
}
}
.Join() performs inner join, but you want left outer join, so forget about .Join()
Code I've provided gives you the result you expect. But maybe you need to add one more Select to form datastructure you need.

Related

Linq query to group by with a limit

I have a simple table in the following structure.
I want to write a LINQ expression to fetch only 5 records always. This 5 should be "Gold" if available. Otherwise add "Bronze" to make it 5. If it still not 5 then add "Silver" to the list. But total results returned should be 5. It should be good in terms of performance.
I tried basic linq but no luck. Any help is highly appreciated.
Class :
public class Option {
public int Id {get;set;
public string Name {get;set;}
public string Priority {get;set;}
}
dbContext.Options would create a connection to database table through ORM and we can apply linq expressions there.
Attempt : dbContext.Options.OrderByDescending(o => o.Priority).GroupBy(a => a.Priority)
this returns grouped result by priority. But i want to include the logic i needed inside this expression.
You want to assign a sort value to each string so that they are ordered. You can do this by assigning the integer 0 to Gold, 1 to Bronze, and 2 for Silver (other).
You then use Take to just get the first 5 records.
// ordered by gold, bronze, silver
var result = dbContext.Options
.OrderBy(o => o.Priority == "Gold" ? 0 : o.Priority == "Bronze" ? 1 : 2)
.Take(5)
.ToList();
It should be good in terms of performance.
Then you could consider using raw SQL to filter the records in the original query that is executed against the database, e.g.:
dbContext.Options.SqlQuery("SELECT TOP 5 * FROM [Option] ORDER BY CASE WHEN [Priority] = 'Gold' THEN 1 WHEN [Priority] = 'Bronze' THEN 2 WHEN [Priority] = 'Silver' THEN 3 ELSE 4 END").ToArray();
Maximal performance and LINQ seldom go hand in hand when it comes to querying databases.
Let Priority be an enum, orderby it and take 5.
class Option
{
public int Id { get; set; }
public string Name { get; set; }
public Priority Priority { get; set; }
}
enum Priority
{
Gold = 0,
Silver,
Bronze
}
static void Main(string[] args)
{
var list = new List<Option>()
{
new Option { Id = 1, Name = "Bob", Priority = Priority.Gold },
new Option { Id = 2, Name = "Rob", Priority = Priority.Gold },
new Option { Id = 2, Name = "David", Priority = Priority.Bronze },
new Option { Id = 2, Name = "Adam", Priority = Priority.Bronze },
new Option { Id = 2, Name = "Jack", Priority = Priority.Silver },
new Option { Id = 2, Name = "Josh", Priority = Priority.Silver },
new Option { Id = 2, Name = "Peter", Priority = Priority.Silver },
new Option { Id = 2, Name = "Max", Priority = Priority.Silver },
new Option { Id = 2, Name = "Steve", Priority = Priority.Silver },
};
var newList = list.OrderBy(l => l.Priority).Take(5);
}
List<Option> top5 = participants.OrderBy(part => {
switch(part.Priority) {
case "Gold": return 1;
case "Bronze": return 2;
case "Silver": return 3;
default: return 4;
}
}).Take(5).ToList();
If the list is shorter than 5, just order them, if that is needed.
See code below :
List<string> rank = new List<string>() { "Gold", "Bronze","Silver" };
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Priority", typeof(string));
dt.Rows.Add(new object[] { 9, "Steve", "Silver" });
dt.Rows.Add(new object[] { 8, "Max", "Silver" });
dt.Rows.Add(new object[] { 7, "Peter", "Silver" });
dt.Rows.Add(new object[] { 6, "Josh", "Silver" });
dt.Rows.Add(new object[] { 5, "Jack", "Bronze" });
dt.Rows.Add(new object[] { 4, "Adam", "Bronze" });
dt.Rows.Add(new object[] { 3, "David", "Gold" });
dt.Rows.Add(new object[] { 1, "Bob", "Gold" });
dt.Rows.Add(new object[] { 2, "Rob", "Gold" });
DataRow[] results = dt.AsEnumerable().OrderBy(x => rank.IndexOf(x.Field<string>("Priority"))).Take(5).ToArray();

How to put lists together into json in C#?

I have this code here in C# (just a sample):
var list1 = ( from x
join y
select new
{
id, name
});
var counta = list1.count;
if (counta > 0)
{
for (var i = 0; i < counta; i++)
{
var userid = list1[i].id;
var user = user.Where(a => a.id == user).Select(a => a.userid).FirstOrDefault();
var list2 = (
from f
join g.Where(a => a.userid = user)select new
{
hourId, hourName
}
);
if (list2 > 0)
{
foreach (var product in list2)
{
var list3 = (
from p.
where (a => a.id == user)join q.
where (a => a.id == user)select new
{
productId, productName
});
}
}
}
}
Here is list1,list2,list3 relationship: list1 has many list2, list2 has many list3 and here is the JSON that I expected to return:
[
{
"list1": {
"id": 1,
"name": "Adam",
"list2": [
{
"hourId": 1,
"hourName": "08:00",
"list3": [
{
"productId": 1,
"productName": "Candy"
},
{
"productId": 2,
"productName": "Cookie"
}
]
},
{
"hourId": 2,
"hourName": "09:00",
"list3": [
{
"productId": 1,
"productName": "Candy"
},
{
"productId": 2,
"productName": "Cookie"
}
]
}
]
}
}
]
So my question here is how can I put list1, list2, list3 together to return the below JSON? Do I have to join 3 lists in LINQ again?
Thank you!

Custom Sort in datagridview in winform

I have data table having rows like
ID Name
2 A
4 B
3 C
5 D
1 E
List order = new List() { "1", "3", "2", "5", "4" }
--------------order by list-----------------
ID Name
1 E
3 C
2 A
5 D
4 B
can anyone help to implement this.. I am using DataTable in Winforms.
Another solution to the already given ones, would be to loop your order list and then sort your source list.
// source list
List<Foo> lSource = new List<Foo>() {
new Foo() { ID = 2, Name = "A" },
new Foo() { ID = 4, Name = "B" },
new Foo() { ID = 3, Name = "C" },
new Foo() { ID = 5, Name = "D" },
new Foo() { ID = 1, Name = "E" },
};
// order list
List<int> order = new List<int>() { 1, 3, 2, 5, 4 };
// loop order list and sort source list
order.ForEach(x =>
{
lSource = lSource.OrderBy(g => g.ID == x).ToList();
});
// set datasource
dataGridView1.DataSource = lSource;
I just added a class Foo containing an int ID and a string Name, because you didn't share your whole code.
I think you can join your order and your datatable with AsEnumerable method and on on part you can equalize both of them and select rows, then you can generate a DataTable from that query with CopyToDataTable method.
var dt = new DataTable();
var dc = new DataColumn() { ColumnName = "ID", DataType = typeof(string) };
dt.Columns.Add(dc);
dc = new DataColumn() { ColumnName = "Name", DataType = typeof(string) };
dt.Columns.Add(dc);
dt.Rows.Add(new object[] { "2", "A" });
dt.Rows.Add(new object[] { "4", "B" });
dt.Rows.Add(new object[] { "3", "C" });
dt.Rows.Add(new object[] { "5", "D" });
dt.Rows.Add(new object[] { "1", "E" });
List<string> order = new List<string>() { "1", "3", "2", "5", "4" };
var query = from item in order
join row in dt.AsEnumerable() on item equals row.Field<string>("ID")
select row;
var result = query.CopyToDataTable();
result will be;
I'm not sure this is the best way or not but this seems to fit with your case.
You can join both lists (the one with items and the one with sorted id's) and then select the items:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var list = new List<Item>{
new Item { Id = 2, Text = "A" },
new Item { Id = 4, Text = "B" },
new Item { Id = 3, Text = "C" },
new Item { Id = 5, Text = "D" },
new Item { Id = 1, Text = "E" }
};
var sortorder = new List<int> { 1, 3, 2, 5, 4 };
var sortedlist = sortorder.Join(list, x => x, y => y.Id, (x,y) => y);
foreach(var item in sortedlist)
Console.WriteLine("{0} {1}", item.Id, item.Text);
}
}

Is it possible to transpose rows to columns in C#.net and listing data?

I have list of two different items like Employees and Departments, both having many to many relations.
Employees:
EmpID EmpName
1 emp1
2 emp2
3 emp3
Departments:
DeptID DeptName
1 dept1
2 dept2
3 dept3
Relation Table
relationID empID DeptID
1 1 1
2 1 2
3 2 3
4 3 1
5 3 3
And my requirements: and department are not fixed
empID Name Dept1 Dept2 Dept3
1 emp1 yes yes No
2 emp2 no no yes
3 emp3 yes no yes
Is it possible to do, if yes please light me thanks.
Json data will be, emplist: [{"empID": "1", "name": "emp1", "Dept1": "yes", "dept2": "yes", "Dept3": "yes"}, {"empID": "2", "name": "emp2", "Dept1": "no", "dept2": "no", "Dept3": "yes"}, {"empID": "3", "name": "emp3", "Dept1": "yes", "dept2": "no", "Dept3": "yes"}];
Thanks in advance :)
Try this linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication58
{
class Program
{
static void Main(string[] args)
{
DataTable employeesTable = new DataTable();
employeesTable.Columns.Add("EmpID", typeof(int));
employeesTable.Columns.Add("EmpName", typeof(string));
employeesTable.Rows.Add(new object[] {1, "emp1"});
employeesTable.Rows.Add(new object[] {2, "emp2"});
employeesTable.Rows.Add(new object[] {3, "emp3"});
DataTable departmentTable = new DataTable();
departmentTable.Columns.Add("DeptID", typeof(int));
departmentTable.Columns.Add("DeptName", typeof(string));
departmentTable.Rows.Add(new object[] {1, "dept1"});
departmentTable.Rows.Add(new object[] {2, "dept2"});
departmentTable.Rows.Add(new object[] {3, "dept3"});
DataTable relationTable = new DataTable();
relationTable.Columns.Add("RelationID", typeof(int));
relationTable.Columns.Add("EmpID", typeof(int));
relationTable.Columns.Add("DeptID", typeof(int));
relationTable.Rows.Add(new object[] {1, 1, 1});
relationTable.Rows.Add(new object[] {2, 1, 2});
relationTable.Rows.Add(new object[] {3, 2, 3});
relationTable.Rows.Add(new object[] {4, 3, 1});
relationTable.Rows.Add(new object[] {5, 3, 3});
var joinTables =
(from r in relationTable.AsEnumerable()
join e in employeesTable.AsEnumerable() on r.Field<int>("EmpID") equals e.Field<int>("EmpID")
join d in departmentTable.AsEnumerable() on r.Field<int>("DeptID") equals d.Field<int>("DeptID")
select new {
relationID = r.Field<int>("RelationID"),
employeeID = e.Field<int>("EmpID"),
employeeName = e.Field<string>("EmpName"),
department = d.Field<string>("DeptName")
})
.ToList();
var results = joinTables.GroupBy(x => x.employeeID).Select(y => new {
employee = new {
empID = y.FirstOrDefault().employeeID,
empName = y.FirstOrDefault().employeeName,
Dept1 = y.Where(z => z.department == "dept1").Any() ? "yes" : "no",
Dept2 = y.Where(z => z.department == "dept2").Any() ? "yes" : "no",
Dept3 = y.Where(z => z.department == "dept3").Any() ? "yes" : "no"
}
}).ToList();
}
}
}
If you have a large list of departments you can do this instead
List<string> departments = departmentTable.AsEnumerable().Select(x => x.Field<string>("DeptName")).Distinct().OrderBy(y => y).ToList();
var results = joinTables.GroupBy(x => x.employeeID).Select(y => new {
employee = new {
empID = y.FirstOrDefault().employeeID,
empName = y.FirstOrDefault().employeeName,
departments = departments.Select(d => y.Where(z => z.department == d).Any() ? d + ": yes" : d + ": no").ToList()
}
}).ToList();

Linq How to Join and get 2 tables values

I have 2 tables and i want to match up 2 Id values.
First table
Id - 1, 2, 3, 4, 5
DepartmentId - 2, 4, 5, 2, 1
Second table
Id- 1, 2, 10, 30, 40
I want to match up first table's Id's with second table's Id's so i can get DepartmentId values.
I need to get this virtual result:
Id- 1, 2, 10, 30, 40
DepartmentId -2, 4, null, null, null
Here is my code:
for (int i = 0; i < model1.Count(); i++)
{
model1[i].DepartmentId= model2.FirstOrDefault(k => k.Id== model1[i].Id).DepartmentId;
}
I get this error:
An exception of type 'System.NullReferenceException' occurred in
IYP.UserInterfaceLayer.dll but was not handled in user code
I think loop fails because of it can't find 10, 30, 40 Id values. If my Id values are same in 2 tables( Id = 1,2,3,4,5) loop works.
How can i do this with Linq?
You are basically looking for Left Join in LINQ. Try this:-
var query = from emp2 in Employee2
join emp1 in Employee1
on emp2.Id equals emp1.Id into allEmployees
from result in allEmployees.DefaultIfEmpty()
select new
{
ID = emp2.Id,
DeptID = result == null ? "No Department" : result.DepartmentId.ToString()
};
Where I have used following types:-
var Employee1 = new[]
{
new { Id = 1, DepartmentId = 2 },
new { Id = 2, DepartmentId = 4 },
new { Id = 3, DepartmentId = 5 },
new { Id = 4, DepartmentId = 2 },
new { Id = 5, DepartmentId = 1 },
};
var Employee2 = new[]
{
new { Id = 1 },
new { Id = 2 },
new { Id = 10 },
new { Id = 30 },
new { Id = 40 },
};
Complete Working Fiddle.
You should use the Join LINQ extension method. In the form of query syntax (which I believe is more readable for this case) it will look like:
var matchedValues =
from second in model2
join first in model1
on second.Id equals first.Id
into temp
from tempFirst in temp.DefaultIfEmpty()
select
new
{
second.Id,
DepartmentId = tempFirst == null ? null : tempFirst.DepartmentId
};
You join on the Id property and for any value you don't find in the model1, you use a default (DefaultIfEmpty call). Then you choose the resulting DepartmentId based on the join result.
try this
List<long> idlist=model2.tolist().select(t=>t.Id);
List<long> depIdList=model1.where(t=>idlist.contains(t.id)).toList();
I am going to assume that model1 and model2 are both IEnumerable. In that case the following should work.
var result = from x in model2
select
new Model1Type {DepartamentId = x,
Value=
model1.FirstOrDefault(y=>y.DepartamentId==x)
.Select(y=>y.Value)};
This is called Lamq :D
Hope this helps :)

Categories

Resources