Compare Array of IDs Entity Framework - c#

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

Select records that have specific count in specific criteria

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

How to remove item from list which Id is not in list of Id's

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.

how to select record based on max value and where clause in LINQ to Entities

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

How to get students that are only in selected batch using linq or sql?

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

linq query repeated columns in a list

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

Categories