Entity framework Group by query - c#

Let's say I have a table with the following data:
Id | title | image | page
-------------------------
1 test a.jpg 1
2 test b.jpg 2
3 test 1 c.jpg 1
4 test 1 d.jpg 2
How would I go about grouping the data by title and retrieving the first results. Like so:
Id | title | image | page
-------------------------
1 test a.jpg 1
3 test 1 c.jpg 1
What I have tried so far but without luck is:
var result = _db.Records.Select(r => new Records
{
Id = r.Id,
title = r.title,
image = r.image,
page = r.page
}).OrderByDescending(x => x.Id)
.GroupBy(x => x.title)
.Select(x => x.First()).AsQueryable();
Am I going about this the right way? Any help appreciated.

Why order by and why return AsQueryable? This is what I have done. If you must return a queryable, appending AsQueryable() will still work.
Records.GroupBy (r => r.Title)
.Select (r =>r.First ())

The first Select doesn't seem to do anything. You already have Records and you're selecting Records.
The second Select is also not needed. Instead of calling x => x.First() why not call First() ? Or am I missing something?
var result = _db.Records
//.OrderByDescending(x => x.Id)
.GroupBy(x => x.title)
.First();
Edit: the OrderBy is doing work that is negated (somewhat) by the GroupBy
Edit 2: The above will only get the first group. So the x => x.First() was correct:
var result = _db.Records
.GroupBy(x => x.title)
.Select(group => group.First());

var results
= _db.Records.GroupBy(
i => i.title,
(key, group) => group.First()
)
Hope this helps

Related

Determine Duplicate data using LINQ to EF

I have a dataset that i want to groupby to determine duplicate data.
Example i have a dataset that looks like this.
|id | Number | ContactID
1 1234 5
2 9873 6
3 1234 7
4 9873 6
Now i want to select data that has more than one occurrence of Number but only if the ContactID is not the same.
So basically return
| Number | Count |
1234 2
Any help would be appreciated using LINQ to EF, thanks.
Update:
All thanks to #DrCopyPaste, as he told me that I misunderstood your problem. Here is the correct solution:-
var result = from c in db.list
group c by c.Number into g
let count = g.GroupBy(x => x.ContactID).Where(x => x.Count() == 1).Count()
where count != 0
select new
{
Number = g.Key,
Count = count
};
Sample Fiddle.
This query avoids making a custom IEqualityComparer as if I remember correctly don't think they play well with EF.
var results = data.GroupBy(number => number.Number)
.Where(number => number.Count() > 1)
.Select(number => new
{
Number = number.Key,
Count = number.GroupBy(contactId => contactId.ContactId).Count(x => x.Count() == 1)
})
.Where(x => x.Count > 0).ToList();
Fiddle
It does an initial GroupBy to get all Numbers that are duplicated. It then selects a new type that contains the number and a second GroupBy that groups by ContactId then counts all groups with exactly one entry. Then it takes all results whose count is greater than zero.
Have not testing it against EF, but the query uses only standard Linq operators so EF shouldn't have any issues translating it.
Another way of doing this(using 1 level of grouping):
var results = data
.Where(x => data.Any(y => y.Id != x.Id && y.Number == x.Number && y.ContactId != x.ContactId))
.GroupBy(x => x.Number)
.Select(grp => new { Number = grp.Key, Count = grp.Count() })
.ToList();
Fiddle

LINQ get Highest value and + 1

I'm newbie to LINQ.
I will like to get know what's the highest value for 'Question Position' and i want increase it by 1 for new Question and save it into database from MVC 4 view.
My db data : (highest position value is 2)
====================
Question | Position
====================
Q1 | 1
Q2 | 2
After added new Question : ( increment the highest position (2) + 1 )
====================
Question | Position
====================
Q1 | 1
Q2 | 2
Q3 | 3
My Code :
var query =
db.SURV_Question_Model
.Where(r => r.Question_Survey_ID == viewModel.Survey_ID)
.GroupBy(r => new { r.Question_Position })
.Select(grp => grp.OrderByDescending(i => i.Question_Position).FirstOrDefault());
After i get the highest value from query, can i do something like below?
* int i = query.Question_Position + 1 ???
Appreciate your guidance.
You can use Max:
var maxId = db.SURV_Question_Model
.Where(r => r.Question_Survey_ID == viewModel.Survey_ID)
.Max(x => x.Position);
But, if there is not any record it will throw an exception. So, it will be better to change your code as:
var maxId = ...Max(x => (int?)x.Position) ?? 0;
You don't need the GorupBy method, this should be enough:
var maxId =
db.SURV_Question_Model
.Where(r => r.Question_Survey_ID == viewModel.Survey_ID)
.OrderByDescending(x => x.Position)
.FirstOrDefault());
I actually don't know the usage of the where condition, I left there anyway.

Filter some unique Data with LINQ and C#

i am very new with C# and MVC.
My Problem:
I have a list OF IDs
int[] mylist = {10, 23}
I try to query some data from DB
var result = db.tableName.Where(o => mylist.Any(y => y == o.item_ID && o.readed)).ToList();
This is what I get with the query:
item_ID Product_ID readed
277 1232 1
277 1233 1
277 1235 1
280 1235 1
What I need is:
item_ID Product_ID readed
277 1235 1
280 1235 1
If I change "any" to "all" i don't get any results, but I have definitely one item where the condition fits.
I think its more like make a query with id 277, then a query with 280 and then merge the list and return only where where "Product_ID" match.
Any ideas?
I assume that what you need is this:
var temp = db.tableName.Where(o => mylist.Any(y => y == o.item_ID && o.readed))
.ToList();
// Find the Product_id which appeared more than one time
// By the way, this assumes that there is at least one product_Id whihc has appeared more than one time
var neededProductID = temp.GroupBy(x => x.Product_ID)
.Where(x => x.Count() > 1)
.First()
.Key;
// Filter the result by neededProductID
var result = temp.Where(x => x.Product_ID == neededProductID).ToList();
Also, if there could be more tha one Product_ID which has apperaed more than one time, then you can consider this:
var neededProductID = temp.GroupBy(x => x.Product_ID)
.Where(x => x.Count() > 1)
.Select(x => x.Key)
.ToList();
var result = temp.Where(x => neededProductID.Any(y => y == x.Product_ID)).ToList();
By the way, you don't need All(). It tells you if all the elements in a collection match a certain condition.
You can use the following
var result = db.tableName.Where(o => mylist.conains(o.item_ID)
&& o.readed).ToList();

How to get first record in each group using Linq

Considering the following records:
Id F1 F2 F3
-------------------------------------------------
1 Nima 1990 10
2 Nima 1990 11
3 Nima 2000 12
4 John 2001 1
5 John 2002 2
6 Sara 2010 4
I want to group by based on the F1 field and sort by Id and get all fields from the first record of group similar to these records:
Id F1 F2 F3
-------------------------------------------------
1 Nima 1990 10
4 John 2001 1
6 Sara 2010 4
How can I do this using linq?
var result = input.GroupBy(x => x.F1, (key,g) => g.OrderBy(e => e.F2).First());
var res = from element in list
group element by element.F1
into groups
select groups.OrderBy(p => p.F2).First();
The awnser of #Alireza is totally correct, but you must notice that when using this code
var res = from element in list
group element by element.F1
into groups
select groups.OrderBy(p => p.F2).First();
which is simillar to this code because you ordering the list and then do the grouping so you are getting the first row of groups
var res = (from element in list)
.OrderBy(x => x.F2)
.GroupBy(x => x.F1)
.Select()
Now if you want to do something more complex like take the same grouping result but take the first element of F2 and the last element of F3 or something more custom you can do it by studing the code bellow
var res = (from element in list)
.GroupBy(x => x.F1)
.Select(y => new
{
F1 = y.FirstOrDefault().F1;
F2 = y.First().F2;
F3 = y.Last().F3;
});
So you will get something like
F1 F2 F3
-----------------------------------
Nima 1990 12
John 2001 2
Sara 2010 4
Use it to achieve what you want. Then decide which properties you want to return.
yourList.OrderBy(l => l.Id).GroupBy(l => new { GroupName = l.F1}).Select(r => r.Key.GroupName)
var res = (from element in list)
.OrderBy(x => x.F2).AsEnumerable()
.GroupBy(x => x.F1)
.Select()
Use .AsEnumerable() after OrderBy()
Another way:
var result = input.GroupBy(i => i.F1).Select(g => g.First());
You can group by multiple fields:
var result = input.GroupBy(i => new {i.F1, i.F2}).Select(g => g.First());
If you need keep order, then use lookup:
var result = input.ToLookup(i => i.F1).Select(g => g.First());
It's not exactly what you were looking for, but sometimes we look for the wrong thing because we don't know what exists. So my solution I find the most intuitiv:
var dict =
input.OrderByDescending(x => x.Id)
.GroupBy(x => x.F1)
.ToDictionary(x => x.Key, x => new { x.First().F1, x.First().F2, x.First().F3});
First order, then group – straight forward. Now the result of that will be a list of key-type pairs❊. As our table was already sorted, we can just pick the first entry. Also I advise to put the result in a dictionary with the ID as accessor, it's a very fast data structure to access and still flexible. Now you can access it using
dict[4].F1 // returns Nima
❊ technically its IEnumerable<IGrouping<int, yourType>>

Multiple group by with aggregate in Linq

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

Categories