24

I have the following simple table with ID, ContactId and Comment.

enter image description here

I want to select records and GroupBy contactId. I used this LINQ extension method statement:

Mains.GroupBy(l => l.ContactID)
 .Select(g => g.FirstOrDefault())
 .ToList()

It returns record 1 and 4. How can I use LINQ to get the ContactID with the highest ID? (i.e. return 3 and 6)

codewitharefin
  • 1,398
  • 15
  • 24
RaelB
  • 3,301
  • 5
  • 35
  • 55

5 Answers5

47

You can order you items

Mains.GroupBy(l => l.ContactID)
.Select(g=>g.OrderByDescending(c=>c.ID).FirstOrDefault()) 
.ToList()
tym32167
  • 4,741
  • 2
  • 28
  • 32
  • 1
    Despite this query will return correct data, it results in compilation into K+1 SQL statements, where K is number of keys (use LINQpad to see it). I faced same problem today and solved it this way: ` var lastCommentIDs = Mains.GroupBy(k => k.ContactID, e => e.ID, (k, e) => new { k, lastID = e.Max(x => x)}); var result = lastCommentIDs.Join(Mains, o => o.lastID, i => i.ID, (o, i) => new { ContactID = o.k, LastComment = i.Comment).ToList(); ` – Dmytro Dec 12 '18 at 14:04
  • Hi, @Dmitrij. Thanks for feedback. Linqpad is using Linq to SQL, did you tried Linq to entities with this? – tym32167 Dec 12 '18 at 14:15
  • Hi, @tym32167. yes, I had an attempt to jump into EF, not sure wha exactly was wrong, but EF produced ugliest and absolutely inefficient SQL queries out of LINQ statements. Probably I did something wrong, not sure but the fact is: LINQ2SQL produces more predictable queries than EF. (at least I understand what will happen when I'm executing paticular query using LINQ2SQL) – Dmytro Dec 12 '18 at 22:11
8

Use OrderByDescending on the items in the group:

Mains.GroupBy(l => l.ContactID)
    .Select(g => g.OrderByDescending(l => l.ID).First())
    .ToList();

Also, there is no need for FirstOrDefault when selecting an item from the group; a group will always have at least one item so you can use First() safely.

RePierre
  • 9,358
  • 2
  • 20
  • 37
6

Perhaps selecting with Max instead of OrderByDescending could result into improving of performance (I'm not sure how it's made inside so it needs to be tested):

var grouped = Mains.GroupBy(l => l.ContactID);
var ids = grouped.Select(g => g.Max(x => x.Id));
var result = grouped.Where(g => ids.Contains(g.Id));

As I assume it could result into a query that will take MAX and then do SELECT * FROM ... WHERE id IN ({max ids here}) which could be significantly faster than OrderByDescending.

Feel free to correct me if I'm not right.

Ivan Yurchenko
  • 3,762
  • 1
  • 21
  • 35
3

OrderByDescending

Mains.GroupBy(l => l.ContactID)
.Select(g=>g.OrderByDescending(c=>c.ID).FirstOrDefault()) 
.ToList()

is your best solution

It orders by the highest ID descending, which is pretty obvious from the name.

0

You could use MoreLinq like this for a shorter solution:

Main.OrderByDescending(i => i.Id).DistinctBy(l => l.ContactID).ToList();
Jesse
  • 3,522
  • 6
  • 25
  • 40
Mous Kamel
  • 257
  • 1
  • 2
  • 11