Select max as second criteria with Linq - c#

i'm trying to get all rows who has some field = max of that field
to be able to explain my doubt, I will leave an example of model
class model
{
[Key]
[Column(Order=0)]
public int Key { get; set; }
[Key]
[Column(Order=1)]
public int Revision { get; set; }
public string OtherStuff { get; set; }
}
A data example could be
|Key|Revision |Otherstuff |
|1 |1 |A |
|1 |2 |B |
|1 |3 |C |
|2 |1 |D |
|2 |2 |E |
|3 |1 |F |
What i want to get:
|Key|Revision |Otherstuff |
|1 |3 |C |
|2 |2 |E |
|3 |1 |F |
I try the solutions i could find here but i couldn't make it work
This it what i got
IQueryable<model> data = dbContext.model;
data = data.GroupBy(x => x.Revision ).OrderByDescending(x => x.Key).SelectMany(x => x)
//Applying logic to filter the data
data = data.Where(...)
My main problem is that it is a filtering method, and when using SelectMany the database is disposed and I can not continue modifying the list as I need
How i supose to do it?
Thanks!
Solved
data = data.GroupBy(x => x.Key).Select(x => x.OrderByDescending(d => d.Revision).FirstOrDefault());
Thanks all responses!

Try this
data = data..GroupBy(x => x.Key).OrderByDescending(x => x.Key).Select(x => new { Key = x.Key, Revision = x.Count() });

var result = data.GroupBy(x => x.Revision).Select(x => x.First())
if you try to do any other operations after the first query (such as select a subset of columns) - you need to use x.FirstOrDefault

data = data.GroupBy(x => x.Revision).Select(revisitions => revisitions.OrderByDescending(x => x.Key).First())

Related

How to convert this SQL query to LINQ or Lambda expression in C#?

I have the following simple table:
table : Inventory
+-------+-----------+-----------+
| Id | ProductId | cost |
+-------+-----------+-----------+
| 1 | 1 | 10 |
| 2 | 2 | 55 |
| 3 | 1 | 42 |
| 4 | 3 | 102 |
| 5 | 2 | 110 |
+-------+-----------+-----------+
I have the following SQL query:
SELECT T.Id
FROM Inventory AS T INNER JOIN
(SELECT ProductId
FROM Inventory
GROUP BY ProductId
HAVING (COUNT(*) > 1)) AS S ON T.ProductId = S.ProductId
This works to give me all of the Ids where a duplicate ProductId exists. Using the above table, this query would return Ids { 1,2,3,5 }, which is exactly what I want.
I tried converting this into a Lambda expression, but it continually fails with the join. Can anyone get me started and point me in the right direction to write this expression?
This is what I have tried:
var q = inventory.Join( inventory.GroupBy( o => o.ProductId ).Where( o => o.Count( ) > 1 ), g => g.ProductId, gb => gb.Key, ( g, gb ) => g.Id ).ToList( );
You need to use somthing like this:
var result = Inventory
.GroupBy(x => x.ProductId)
.Where(x => x.Count() > 1)
.SelectMany(x => x.ToList())
.Select(x => x.Id);

Get Max() rows per Revision Number in LinQ

I have this result set:
Company_ID | State | City | Rev |
Company 1 | S-A | C-1 | 1 |
Company 2 | S-B | C-2 | 1 |
Company 1 | S-A | C-3 | 2 |
The result set that I wanted is
Company_ID | State | City | Rev |
Company 1 | S-A | C-3 | 2 |
Company 2 | S-B | C-2 | 1 |
Here's the query that I tried:
var list = (from coy in TBL_COMPANY
where coy.IsActive == "1"
select coy).ToList()
.Select(coy => new
{
coy.Company_ID,
coy.State,
coy.City,
coy.Rev
});
var companyList = list.GroupBy (grp => new
{
grp.Company_ID,
grp.State,
grp.City
})
.Select(cpy => new
{
CompanyID = cpy.Key.Company_ID,
State = cpy.Key.State,
City = cpy.Key.City,
Rev = cpy.Max(c => c.Rev)
}).ToList();
However, I am not able to get the result set I wanted. My suspicion there is because of the City because it is included. However, I need these columns (except for the Rev). Is there any way to do this?
The required result doesn't fit you grouping. There are 3 different City items which should lead to 3 result items.
I'd say Jon Skeet's comment about Grouping only by Company_ID is correct.
Code (group by Company_ID and pick the items with the highest Rev):
var result = TBL_COMPANY.Where(x => x.IsActive == "1")
.GroupBy(x => x.Company_ID)
.Select(x => x.OrderByDescending(y => y.Rev).First())
.Select(x=> new {x.Company_ID, x.State, x.City, x.Rev})
.ToList();

Entity Framework Group By with Max Date and count

I have the following SQL
SELECT Tag , COUNT(*) , MAX(CreatedDate)
FROM dbo.tblTags
GROUP BY Tag
Which outputs the following:
+-----------------+------------------+-------------------------+
| Tag | (No column name) | (No column name) |
+-----------------+------------------+-------------------------+
| a great tag | 1 | 2015-04-01 18:30:31.623 |
| not a test | 1 | 2015-04-01 17:46:09.360 |
| test | 5 | 2015-04-01 18:13:17.920 |
| test2 | 1 | 2013-03-07 16:53:54.217 |
+-----------------+------------------+-------------------------+
I'm trying to replicate the output of that query using EntityFramework.
I have the following logic which works:
var GroupedTags = Tags.GroupBy(c => c.Tag)
.Select(g => new
{
name = g.Key,
count = g.Count(),
date = g.OrderByDescending(gt => gt.CreatedDate).FirstOrDefault().CreatedDate
})
.OrderBy(c => c.name);
But takes horribly long to execute compared to the raw SQL query. Any suggestions on how to optimise my approach? It somehow feels wrong.
If you want a max, use the Max() Linq method:
var GroupedTags = Tags.GroupBy(c => c.Tag)
.Select(g => new
{
name = g.Key,
count = g.Count(),
date = g.Max(x => x.CreatedDate)
})
.OrderBy(c => c.name);

Entity Framework select one of each group by date

I have a table like this (Table name: Posts):
+----+--------------------------+-------+------------+
| id | content | type | date |
+----+--------------------------+-------+------------+
| 0 | Some text | TypeA | 2013-04-01 |
| 1 | Some older text | TypeA | 2012-03-01 |
| 2 | Some even older texttext | TypeA | 2011-01-01 |
| 3 | A dog | TypeB | 2013-04-01 |
| 4 | And older dog | TypeB | 2012-03-01 |
| 5 | An even older dog | TypeB | 2011-01-01 |
+----+--------------------------+-------+------------+
Using a LINQ expression I want to find the newest content of each type, so the result should be
Some text | TypeA
A dog | TypeB
I have tried a few things but no point in pointing out non-working expressions.
If you want to get the whole Posts. You can try this:
var query = Posts.GroupBy(p => p.Type)
.Select(g => g.OrderByDescending(p => p.Date)
.FirstOrDefault()
)
Or using temporary result and predicate
var tmp = posts.GroupBy(x => x.type).Select(x => new {x.Key, date = x.Max(g => g.date)).ToArray();
var filter = PredicateBuilder.False<Post>();
foreach (var item in tmp)
{
filter = filter.Or(x => x.type == item.Key && x.date == item.date);
}
var newestPosts = posts.Where(filter);
I suppose you can group your Posts rows by type and then select first content from descending ordered by date collection of that type
from row in Posts
group row by row.type
into g
select new
{
Content = (from row2 in g orderby row2.date descending select row2.content).FirstOrDefault(),
Type = g.Key
}
From memory, something like this should do it
var data = context.Posts.GroupBy(p => p.Type)
.Select(g => new {
Type = g.Key,
Date = g.OrderByDescending(p => p.Date)
.FirstOrDefault()
}
This would give you a new anonymous type, but you can always map it to a class

Calculating difference between different columns in different rows

I have a table that records what happens to a vehicle during a visit. For each visit, there is are multiple rows to denote what has been done. The table looks like this
VisitID | ActionID | StartTime | EndTime
0 | 0 | 1/1/2013 | 1/2/2013
1 | 0 | 1/2/2013 | 1/4/2013
1 | 1 | 1/4/2013 | 1/7/2013
2 | 0 | 1/4/2013 | 1/5/2013
2 | 1 | 1/5/2013 | 1/6/2013
2 | 2 | 1/6/2013 | 1/7/2013
I wish to construct a LINQ query capable of getting the amount of time a visit took. TotalTime is calculated by first finding the first and last (lowest and highest) ActionID, then last.EndTime - first.StartTime. Expected results:
VisitID | TotalTime
0 | 1
1 | 5
2 | 3
I can generate my expected results by doing
var first = db.Visits.Where(v => v.ActionID == 0)
var last = db.Visits.GroupBy(x => x.VisitID).Select(g => g.OrderByDescending(x => x.ActionID).First())
first.Join(last, f => f.VisitID, l => l.VisitID, (f, l) new{ VisitID = Key, TotalTime = l.EndTime - f.StartTime});
I really don't like the hack I used to get the last ActionID, and I would really like to be able to do this within 1 LINQ statement. What do I need to do to achieve this?
I think this should work...
var result = db.Visits.GroupBy(v => v.VisitID)
.Select(g => new
{
VisitId = g.Key,
TotalTime = g.Max(v => v.EndTime).Subtract(g.Min(v => v.StartTime)).Days
});
Edit: This assumes the actionid doesn't matter so much as the max and min start dates. Here is a different solution where the actionid's are ordered and the first and last Visits are used to calculate the time difference.
var result2 = db.Visits.GroupBy(v => v.VisitID)
.Select(g => new
{
VisitId = g.Key,
TotalTime =
g.OrderBy(v => v.ActionID).Last().EndTime.Subtract(g.OrderBy(v => v.ActionID).First().StartTime).Days
});
db.Visits.GroupBy(v => v.VisitID)
.Select(g => new { VisitId = g.Key,
Days = g.Select(x => x.EndTime.ToOADate()).Sum()
-g.Select(x => x.StartTime.ToOADate()).Sum() });
Used a little hack to add all starts days and end days for each visit and get the difference.

Categories

Resources