How to get the Max() of a Count() with LINQ

前端 未结 4 917
面向向阳花
面向向阳花 2021-02-08 20:45

I\'m new to LINQ and I have this situation. I have this table:

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/         


        
相关标签:
4条回答
  • 2021-02-08 21:23

    Using extension methods:

    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 讨论(0)
  • 2021-02-08 21:29

    I think something along these lines should work:

    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 讨论(0)
  • 2021-02-08 21:32

    This approach:
    1) Groups by Date
    2) For each Date, groups by Range and calculates the Total
    3) For each Date, selects the item with the greatest Total
    4) You end up with your result

    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;
        }
    }
    

    According to LINQ in Action (Chapter 5.3.3 - Will LINQ to Objects hurt the performance of my code?), using the MaxElement extension method is one of the most effecient approaches. I think the performance would be O(4n); one for the first GroupBy, two for the second GroupBy, three for the Count(), and four for loop within MaxElement.

    DrDro's approach is going to be more like O(n^2) since it loops the entire list for each item in the list.

    StriplingWarrior's approach is going to be closer to O(n log n) because it sorts the items. Though I'll admit, there may be some crazy magic in there that I don't understand.

    0 讨论(0)
  • 2021-02-08 21:35

    unfortunately I can't test this at the moment but give this a try:

    List<MyTable> items = GetItems();
    items.Max(t=>t.Range.Distinct().Count());
    
    0 讨论(0)
提交回复
热议问题