2010-11-16 24 views
8

Tôi mới sử dụng LINQ và tôi có tình trạng này. Tôi có bảng này:Làm cách nào để có được giá trị Max() của một Count() với LINQ

ID Date Range 
1 10/10/10 9-10 
2 10/10/10 9-10 
3 10/10/10 9-10 
4 10/10/10 8-9 
5 10/11/10 1-2 
6 10/11/10 1-2 
7 10/12/10 5-6 

Tôi chỉ muốn liệt kê các maximun của hàng mỗi ngày bởi phạm vi, như thế này:

Date Range Total 
10/10/10 9-10 3 
10/11/10 1-2 2 
10/12/10 5-6 1 

Tôi muốn làm điều này bằng cách sử dụng LINQ, bạn có bất kỳ ý tưởng làm thế nào để làm điều này?

Trả lời

1

tiếc là tôi không thể kiểm tra điều này vào lúc này nhưng cung cấp cho một thử này:

List<MyTable> items = GetItems(); 
items.Max(t=>t.Range.Distinct().Count()); 
+0

Bạn có thể giải thích điều này có nghĩa là phải làm gì không? Vì Max trả về một giá trị duy nhất, tôi không chắc nó có thể giúp ích gì cho câu hỏi này. – Jla

6

Tôi nghĩ rằng một cái gì đó dọc theo những dòng nên làm việc:

List<MyTable> items = GetItems(); 
var orderedByMax = from i in items 
        group i by i.Date into g 
        let q = g.GroupBy(i => i.Range) 
          .Select(g2 => new {Range = g2.Key, Count = g2.Count()}) 
          .OrderByDescending(i => i.Count) 
        let max = q.FirstOrDefault() 
        select new { 
         Date = g.Key, 
         Range = max.Range, 
         Total = max.Count 
        }; 
+0

Một vài lỗi chính tả: 1) nó sẽ đọc "nhóm i bởi i.Date" không phải "nhóm i bởi new i.Date" và 2) thiếu dấu phẩy sau "Range = max.Range". Typos sang một bên, điều này hoạt động. Đã cho tôi một thời gian để quấn đầu của tôi xung quanh nó, nhưng nó hoạt động. :) – Ecyrb

+0

@Ecyrb: Tôi đã sửa lỗi chính tả. Tôi phải thừa nhận, đó là một truy vấn phức tạp hơn lần đầu tiên có vẻ như nó sẽ như thế. Tôi rùng mình trước ý nghĩ cố gắng làm điều đó với SQL thẳng. – StriplingWarrior

4

phương pháp khuyến nông Sử dụng:

List<MyTable> items = GetItems(); 

var rangeTotals = items.GroupBy(x => new { x.Date, x.Range }) // Group by Date + Range 
        .Select(g => new { 
           Date = g.Key.Date, 
           Range = g.Key.Range, 
           Total = g.Count() // Count total of identical ranges per date 
           }); 

var rangeMaxTotals = rangeTotals.Where(rt => !rangeTotals.Any(z => z.Date == rt.Date && z.Total > rt.Total)); // Get maximum totals for each date 
+0

Tôi đã học được vài điều từ ví dụ này - cảm ơn! – bwperrin

0

Cách tiếp cận này:
1) Nhóm theo ngày
2) Đối với mỗi ngày, nhóm của Phạm vi và tính toán Tổng
3) Đối với mỗi ngày, chọn mục với vĩ đại nhất Tổng
4) Bạn kết thúc với kết quả của bạn

public sealed class Program 
{ 
    public static void Main(string[] args) 
    { 
     var items = new[] 
     { 
      new { ID = 1, Date = new DateTime(10, 10, 10), Range = "9-10" }, 
      new { ID = 2, Date = new DateTime(10, 10, 10), Range = "9-10" }, 
      new { ID = 3, Date = new DateTime(10, 10, 10), Range = "9-10" }, 
      new { ID = 4, Date = new DateTime(10, 10, 10), Range = "8-9" }, 
      new { ID = 5, Date = new DateTime(10, 10, 11), Range = "1-2" }, 
      new { ID = 6, Date = new DateTime(10, 10, 11), Range = "1-2" }, 
      new { ID = 7, Date = new DateTime(10, 10, 12), Range = "5-6" }, 
     }; 

     var itemsWithTotals = items 
      .GroupBy(item => item.Date) // Group by Date. 
      .Select(groupByDate => groupByDate 
       .GroupBy(item => item.Range) // Group by Range. 
       .Select(groupByRange => new 
       { 
        Date = groupByDate.Key, 
        Range = groupByRange.Key, 
        Total = groupByRange.Count() 
       }) // Got the totals for each grouping. 
       .MaxElement(item => item.Total)); // For each Date, grab the item (grouped by Range) with the greatest Total. 

     foreach (var item in itemsWithTotals) 
      Console.WriteLine("{0} {1} {2}", item.Date.ToShortDateString(), item.Range, item.Total); 

     Console.Read(); 
    } 
} 

/// <summary> 
/// From the book LINQ in Action, Listing 5.35. 
/// </summary> 
static class ExtensionMethods 
{ 
    public static TElement MaxElement<TElement, TData>(this IEnumerable<TElement> source, Func<TElement, TData> selector) where TData : IComparable<TData> 
    { 
     if (source == null) 
      throw new ArgumentNullException("source"); 
     if (selector == null) 
      throw new ArgumentNullException("selector"); 

     bool firstElement = true; 
     TElement result = default(TElement); 
     TData maxValue = default(TData); 
     foreach (TElement element in source) 
     { 
      var candidate = selector(element); 
      if (firstElement || (candidate.CompareTo(maxValue) > 0)) 
      { 
       firstElement = false; 
       maxValue = candidate; 
       result = element; 
      } 
     } 
     return result; 
    } 
} 

Theo LINQ trong hành động (Chương 5.3.3 - LINQ sẽ đối tượng bị tổn thương hiệu suất của mã của tôi?), Sử dụng phương pháp mở rộng MaxElement là một trong những phương pháp hiệu quả nhất. Tôi nghĩ hiệu suất sẽ là O (4n); một cho GroupBy đầu tiên, hai cho GroupBy thứ hai, ba cho Count() và bốn cho vòng lặp trong MaxElement.

Cách tiếp cận của DrDro sẽ giống O (n^2) vì nó lặp lại toàn bộ danh sách cho từng mục trong danh sách.

Stripling Cách tiếp cận của Warrior sẽ tiến gần hơn đến O (n log n) vì nó sắp xếp các mục. Mặc dù tôi sẽ thừa nhận, có thể có một số ma thuật điên rồ ở đó mà tôi không hiểu.

+1

+1. Khi sử dụng LINQ to Objects, đây là một cách tiếp cận tốt. Kể từ khi OP đề cập đến một "bảng" tôi giả sử anh ta đang sử dụng LINQ to Entities hoặc LINQ to SQL, nó sẽ không hỗ trợ các phương thức mở rộng 'MaxElement' tùy chỉnh của bạn. Về hiệu suất của câu trả lời của tôi, tôi tin rằng sự kết hợp của 'OrderBy' và' FirstOrDefault' sẽ cho phép cơ sở dữ liệu tối ưu hóa nó và tránh phân loại toàn bộ bảng. – StriplingWarrior

Các vấn đề liên quan