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()
})
})
Related
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 a list with set of values. I wanted to check if the table has the matching values with the matching count using a LINQ query in c#
This is my list:
List<int> list1 = new List<int>()
{
130011,
130010
};
This is my table
RelEmployeeDepartments:
ID EmpID DeptID
8 4 130011
9 4 130010
10 4 2
18 13 130011
19 13 130010
20 13 1
21 13 2
23 5 130011
24 5 130010
Now i wanted to find all the Employee's who are already assigned to the exact departments list1 has along with the same count. So with the values in my list1 the output shall be EmpID 5 as it has the same values and same count of departments. EmpID 4 & 13 shouldn't be in my output because even though there are matching departments for the employee, the count is different.
This is the working SQL query i came up with:
SELECT EmpID FROM
RelEmployeeDepartments WHERE EmpID IN
(SELECT red.EmpID FROM RelEmployeeDepartments red
GROUP BY red.EmpID HAVING COUNT(red.DeptID) = 2)
AND DeptID IN (130010,130011)
GROUP BY EmpID HAVING COUNT(DeptID) = 2
I managed to get the inner query with LINQ like this but couldn't convert it fully.
var innerQuery = (from red in RelEmployeeDepartments
group red by red.EmpID into red1
where red1.Count().Equals(list1.length)
select red1.Key);
I am wondering how can i convert this to a LINQ query or if there is even a better solution than above one?
you could use a combination of Contain, GroupBy, and Where clauses:
var result = RelEmployeeDepartments
.Where(e => list1.Contains(e.DeptID))
.GroupBy(g => g.EmpId)
.Where(grp => grp.Count() == list1.Count())
above will give you IGrouping, you can then Select and SelectMany to get IEnumerable<int> of EmpID
result.Select(grp => grp.Select(v => v.EmpId)).SelectMany(x => x).Distinct()
Finally after lot of struggle,help with #farzan-mirheydari answer and Using linq to group a table that contains substrings this is my working solution.It's an exact conversion of my SQL query in the question
from red1 in RelEmployeeDepartments
where
(from red in RelEmployeeDepartments group red by new {
red.EmpID
}
into g where g.Count() == list1.Count() select new {
g.Key.EmpID
}).Contains(new {
EmpID = red1.EmpID
}) &&
(list1).Contains(red1.DeptID)
group red1 by new {
red1.EmpID
}
into g
where g.Count() == list1.Count()
select new {
g.Key.EmpID
};
Lambda Code:
RelEmployeeDepartments
.Where(
red1 =>
(RelEmployeeDepartments
.GroupBy(
red =>
new {
EmpID = red.EmpID
}
)
.Where(g => (g.Count() == list1.Count()))
.Select(
g =>
new {
EmpID = g.Key.EmpID
}
)
.Contains(
new {
EmpID = red1.EmpID
}
) &&
list1.Contains(red1.DeptID)
)
)
.GroupBy(
red1 =>
new {
EmpID = red1.EmpID
}
)
.Where(g => (g.Count() == list1.Count()))
.Select(
g =>
new {
EmpID = g.Key.EmpID
}
);
I know it looks complex. If there is a better or easier way, please post it.
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.
I need to display the groupby count according to the descending order of count number followed by order of the last update row (CreatedDateTime desc). How can this be done using LINQ?
TableA.GroupBy(c => c.UserID).Select(
g => new { UserID = g.Key, Count = g.Count() }
).OrderByDescending(c => c.Count)
Here's an example of the intended ordering.
Table A
TableAID UserID TableBID CreatedDateTime
1 1 1 ..
2 1 1 ..
3 2 2 ..
4 2 2 ..
......
Like this (I hope) :-)
TableA.GroupBy(c => c.UserID)
.Select(g => new { UserID = g.Key, Count = g.Count(), g.Max(h => h.CreatedDateTime) })
.OrderByDescending(c => c.Count)
.ThenByDescending(c => c.CreatedDateTime)
Note that I selected the Max(CreatedDateTime). I could have used the Min(CreatedDateTime), but the result would have been different.
I currently have this code:
foreach (var newsToPolitician in news.NewsToPoliticians)
{
var politician = newsToPolitician.Politician;
var votes = (from s in db.Scores
where o.IDPolitician == politician.IDPolitician
&& o.IDNews == IDNews
group o by o.IDAtribute
into g
select new{
Atribute= g.Key,
TotalScore= g.Sum(x => x.Score)
}).ToList();
}
It works alright, but I want to avoid making multiple queries to my database in foreach loop.
My table Scores looks like this:
IDScore | IDNews | IDUser | IDPolitician | IDAtribute | Score
1 40 1010 35 1 1
2 40 1010 35 2 -1
3 40 1002 35 1 1
4 40 1002 35 2 1
5 40 1002 40 1 -1
...
My goal is to aggregate all the scores for all politicians in a news. A news can have up to 7 politicians.
Is it expensive to call my database up to seven times in a foreach loop. I know that isn't best practice so I'm interested is there any way to avoid it in this particular case and make one call to database and then process it on the server side?
Update - Due to user comments have re-jigged to try and ensure aggregation on the server.
In this case we can group on the server by both IDPolitician and IDAttribute and then pull the groups in with ToLookup locally as so:
var result = db.Scores.Where(s => s.IDNews == IDNews)
.Where(s => news.NewsToPoliticians
.Select(n => n.Politician.IDPolitician)
.Contains(s.IDPolitician))
.GroupBy(s => new
{
s.IDPolitician,
s.IDAttribute
},
(k,g ) => new
{
k.IDPolitician,
k.IDAttribute,
Sum = g.Sum(x => x.Score)
})
.ToLookup(anon => anon.IDPolitician,
anon => new { anon.IDAttribute, anon.Sum })
Legacy -
You want to use GroupJoin here, it would be something along the lines of:
var result = news.NewsToPoliticians
.GroupJoin( db.Scores.Where(s= > s.IDNews == IDNews),
p => p.IDPolitician,
s => s.IDPolitician,
(k,g) => new
{
PoliticianId = k,
GroupedVotes = g.GroupBy(s => s.IDAtribute,
(id, group) => new
{
Atribute = id,
TotalScore = group.Sum(x => x.Score)
})
})
.ToList();
However you are at the mercy of your provider as to how it translates this so it might still be multiple queries to get round this you could use something like:
var politicianIds = news.NewsToPoliticians.Select(p => p.IDPolitician).ToList()
var result = db.Scores.Where(s= > s.IDNews == IDNews)
.Where(s => politicianIds.Contains(s.IDPolitician))
.GroupBy(p => p.IDPolitician,
(k,g) => new
{
PoliticianId = k,
GroupedVotes = g.GroupBy(s => s.IDAtribute,
(id, group) => new
{
Atribute = id,
TotalScore = group.Sum(x => x.Score)
})
})
.ToList();
Which hopefully should be at most 2 query (depending on whether NewsToPoliticians is db dependent). You'll just have to try it out and see.
Use a stored procedure and get the SQL server engine to do all the work. You can still use Linq to call the stored procedure and this will minimize all the calls to the database