I have a simple scenario for which i want to write LINQ query, But i am unable to get it right. Here is the scenario:
I have 3 Tables:
STUDENT:
--------------------------
SID Name
---------------------
1 Jhon
2 Mishi
3 Cook
4 Steven
COURSE:
-------------------
CID Name
-------------------
1 Maths
2 Physics
3 Bio
4 CS
STUDENTCOURSE:
---------------------------
SCID SID CID
-----------------------
1 1 1
2 1 2
3 1 4
4 2 1
5 2 2
6 2 3
7 3 1
8 3 4
10 4 2
For this case i want to pass array of course ids to query and return all student those have all the these courses registered against them. What i tried:
int[] cIds = {1,2,4 };
var result = from s in context.Students
where s.StudentCourses.Any(sc=> cIds.Contains(sc.CID))
select s;
But this returns students those registered either of the course id 1,2,4.
Hope you understand my problem.
Thanks for the help.
Try the following:
int[] cIds = {1,2,4 };
var result = from s in context.Students
where cIds.All(id => s.StudentCourses.Any(sc=> sc.CID == id))
select s;
Use this:
int[] cIds = {1,2,4 };
var q = context.StudentCourses.Join(context.Students,
x => x.SId,
x => x.Id,
(sc, s) => new { Student = s, CourseId = sc.CId })
.GroupBy(x => x.Student.Id)
.Where(sc => cIds.All(cid => sc.Any(y => y.CourseId == cid)))
.Select(x => x.FirstOrDefault().Student)
.ToList();
Or if you prefer linq query:
int[] cIds = {1,2,4 };
var q2 = (from s in context.Students
join sc in context.StudentCourses on s.Id equals sc.SId into sCources
where cIds.All(id => sCources.Any(y => y.CId == id))
select s).ToList();
Here is a fiddle for it, using linq-to-objects.
Edit:
I didn't notice that in your model there is a navigation property from Student to StudentCourse, in this case the query will be much simpler and does not need join, and Patrick's answer works perfectly.
Related
Please consider this table:
id Name Year Val1 Val2
-------------------------------------------------------------
1 Japan 2010 20 a
2 Korea 2010 10 a
3 Japan 2010 10 b
4 Germany 2011 22 a
5 Austria 2012 18 a
6 Austria 2012 17 m
7 Germany 2013 11 j
8 Germany 2013 12 j
I want to select records that have just 1 record in a year. This is my desire result:
id Name Year Val1 Val2
------------------------------------------------------------
2 Korea 2010 10 a
4 Germany 2011 22 a
How can I do this with LINQ. Thanks
Make group of records with same [Name, Year] combination, and throw away all groups that have more than one element:
var countries1Record = originalRecords.GroupBy(
record => new
{
Country = record.Name,
Year = record.Year,
})
.Where(group => group.Skip(1).Any())
.Select(group => group.FirstOrDefault());
I use Skip(1).Any() instead of Count() == 1, because it is more efficient: why count all 1000 records, if you can stop counting after the first record?
The final Select selects the one-and-only record in the group
You would need to do a Grouping with a 'Having' clause. The code below should help or point you in the right direction.
var data = new List<Data>();
//Method syntax:
var row = data.GroupBy(k => new { k.Year, k.Name}, v => v)
.Where(grp => grp.Count() == 1)
.SelectMany(o => o)
.ToList();
//Query syntax:
var row2 = (from d in data
group d by new {d.Year, Name = d.Name}
into g
where g.Count() == 1
select g)
.SelectMany(o => o)
.ToList();
Added grouping by Name & Year and Query Syntax. Just a note: Both Query & Method Syntax is linq. Just different ways of writing it.
Hope this helps
Assuming your data is stored in rows:
var results = rows.Where(r1 => !rows.Any(r2 => r1.Name == r2.Name && r1.Year == r2.Year && r1.Id != r2.Id));
I have a table that looks like this:
ID ProductId OrderId
-- --------- --------
1 1 1
2 2 1
3 4 1
4 2 2
5 6 2
6 7 2
First of all I'm getting all ProdictId's that have the same OrderId
var listOfProdictids = db.OrderedProducts.Where(x => x.OrderId == 1).Select(x => x.ProductId).toArray();
I want it to look like 1, 2, 4
I also have a table Product
ID Brand Mark
-- --------- --------
1 Samsung Galaxy s10
2 Apple Iphone 7
3 Xiaomi Mi9
4 Huawei Honor
5 Sony Xperia
What is the best way to get from database all products which Id's are 1, 2, 4?
What I think:
List<ProductVM> products = db.Products.Select(x => x).toList();
to get all the Products to list and somehow delete Id's 3 and 5 which are not in array "listOfProductids".
Or
List<ProductVM> products = db.Products.Where(x => x.id == listOfProductids).Select(x => x).toList();
Not sure that lambda Where(x => x.id != listOfProdictids) works.
The idea is to compare each ProductId with Id's in array. If match than select
Any idea how to do it?
the final list should look like this:
ID Brand Mark
-- --------- --------
1 Samsung Galaxy s10
2 Apple Iphone 7
4 Huawei Honor
Update: based on answer npinti
If I want to do it for 2 orders should it look like this?
var orders = db.Orders.Select(x => x.ProductId).toArray();
to get Array with two orders 1 and 2
than
List<ProductVM> products = new List<ProductVM>();
foreach (ord o in orders)
{
// getting all product id's for first OrderId
HashSet<int> listOfProdictids = new HashSet<int>(db.OrderedProducts.Where(x => x.OrderId == o).Select(x => x.ProductId).toArray());
//getting list of products
var qwe = db.Products.Where(x => !listOfProducts.Contains(x.id)).Select(x => x).toList();
//Add list
products.Add(qwe)
}
return View (products);
You can try and do this:
HashSet<int> listOfProdictids = new HashSet<int>(db.OrderedProducts.Where(x => x.Id == 1).Select(x => x.ProductId).toArray());
And then:
List<ProductVM> products = db.Products.Where(x => !listOfProducts.Contains(x.id)).Select(x => x).toList();
To get the order's all product ID's
var listOfProdictids = db.OrderedProducts.Where(x => x.OrderId == 1).Select(x => x.ProductId).ToArray();
You need to substract to all product's which ordered.
List<ProductVM> removedProducts = db.Products.Where(c=>!listOfProdictids.Contains(c=>c.Id)).ToList();
And remove the list from Products table.
i have following records in my table tbl_JobMaster
ID CIN JobNumber Version
1 ABC 100 1.0
2 ABC 100 2.0
3 ABC 200 1.0
4 ABC 200 2.0
5 ABC 200 3.0
6 XYZ 300 1.0
i want list of records based on CIN column and MAX(Version) and unique JobNumber
for e.g. CIN=ABC
so output should be as follows
ID CIN JobNumber Version
2 ABC 100 2.0
5 ABC 200 3.0
I tried following code but it isn't working
var result=(from job in entity.tbl_JobMaster
where job.CIN=="ABC" && job.Version==entity.tbl_JobMaster.Where(p=>p.ID==job.ID).Max(p=>p.Version)
select job).Distinct();
Here's an example using LINQ extension methods:
var selection = entity.tbl_JobMaster
.Where(job => job.CIN == "ABC")
.GroupBy(job => job.JobNumber)
.Select(group => group
.OrderByDescending(job => job.Version)
.First()
);
Your query should be like below
var result = (from job in entity.tbl_JobMaster
where job.CIN == "ABC" && job.Version == entity.tbl_JobMaster.Where(p => p.CIN == job.CIN && p.JobNumber == job.JobNumber).Max(p => p.Version)
select job).Distinct();
First group by the CIN and JobNumber
var temp = from job in entity.tbl_JobMaster
where job.CIN == "ABC"
group job by new { job.CIN, job.JobNumber } into g
select g;
Then order the items in groups by Version descending, and select the first one
var result = temp.Select(o => o.OrderByDescending(t => t.Version).First());
I have Students,Batches and StudentBatches tables in my database.Student and respective batches will be there in StudentBatches table like below.
Students
ID Name
1 Student'A'
2 Student'B'
3 Student'C'
4 Student'D'
5 Student'E'
Batches
ID Name
1 Batch'A'
2 Batch'B'
3 Batch'C'
StudentBatches
ID StudentID BatchID
1 1 1
2 2 2
3 2 3
4 3 3
5 4 3
6 5 2
My requirement is when I give any batch ID I should get students who are there only in that batch.For example if I give batch Id 3 then I should get 3,4 student ids only because they are not there in any other batch,I should not get 2 student id because that student is also there in batch 2.I hope you understand my requirement.
I have written this query in linq.
from batch_student in context.Student_batches
group batch_student by batch_student.SID into new_batch_student
join student in context.Students on new_batch_student.Key equals student.Id
where new_batch_student.Count() == 1 && new_batch_student.Any(x => x.BID == 3)
select student;
The query is working.But will this have any impact on performance?Is there any query to get that required result?
Another option would be get rid of grouping and add sub-query
var query = from batch in context.Student_batches
join student in context.Students on batch.SID equals student.Id
where batch.BID == 3 &&
!context.Student_batches.Any(x => x.SID == student.Id && x.BID != batch.BID)
select student;
See fiddle
Here is a straightforward approach:
var batchID = 3;
var goodStudentIDs = context.Student_batches
.Where(i => i.BatchID == batchID)
.Select(i => i.StudentID);
var badStudentIDs = context.Student_batches
.Where(i => i.BatchID != batchID)
.Select(i => i.StudentID);
var studentIDs = goodStudentIDs.Except(badStudentIDs);
var students = context.Students.Where(i => studentIDs.Contains(i.ID));
You can combine these four statements into a single statement if that is your desire:
var batchID = 3;
var students = context.Students
.Where(i => context.Student_batches
.Where(j => j.BatchID == batchID)
.Select(j => j.StudentID)
.Except(context.Student_batches
.Where(j => j.BatchID != batchID)
.Select(j => j.StudentID))
.Contains(i.ID));
I have a table similar to the one below.
Branch Dept Product ID Product Val Product Date
Branch 1 Dept 1 ID 1 1 5/23/2013
Branch 1 Dept 1 ID 2 1 5/23/2013
Branch 1 Dept 2 ID 3 1 5/23/2013
Branch 2 Dept 11 ID 4 1 5/23/2013
Branch 2 Dept 11 ID 5 1 5/23/2013
Branch 2 Dept 11 ID 6 1 5/23/2013
Branch 3 Dept 21 ID 7 1 5/23/2013
I am trying to use LINQ(am a rookie to LINQ) to load this as a collection of objects into an object like:
Products = { Branch1 { Dept1 {ID1,ID2},
Dept2 {ID3}},
Branch2 { Dept11 {ID4, ID5, ID6}},
Branch3 { Dept21 {ID7 }
}
And I have trying bit hard working overnight but could not get the right solution. So far I have achieved the following code;
var branches = (from p in ProductsList
select p.Branch).Distinct();
var products = from s in branches
select new
{
branch = s,
depts = (from p in ProductsList
where p.Branch == s
select new
{
dept = p.Dept,
branch = s,
prod = (from t in ProductsList
where t.Branch = s
where t.Dept == p.Dept
select t.ProductID)
})
};
where ProductsList is the list object of the whole table date List
Any help at the earliest is much appreciated. Thanks in advance!
I would go for soemthing like that if you really wanna use linq.
Somethimes, a few foreach are much clearer !
var myDic = ProductList
.GroupBy(m => m.Branch)
.ToDictionary(
m => m.Key,
m => m.GroupBy(x => x.Dept)
.ToDictionary(
x => x.Key,
x => x.Select(z => z.ProductId)));
result will be a
Dictionary<string, Dictionary<string, IEnumerable<string>>>
where first dictionary Key is Branch, inner dictionary key is Dept, and string list are ProductId
which seem to correpond to your wanted result.
Something like this, maybe?
Products.
.Select(prop => prop.Branch)
.Distinct()
.Select(b => new
{
Branch = b,
Departments = Products
.Where(p => p.Branch == b)
.Select(p => p.Dept)
.Distinct()
.Select(d => new
{
Products = Products
.Where(p => p.Department == d)
.Select(p => p.ProductID)
.Distinct()
})
})