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

前端 未结 4 920
面向向阳花
面向向阳花 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: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();
        }
    }
    
    /// 
    /// From the book LINQ in Action, Listing 5.35.
    /// 
    static class ExtensionMethods
    {
        public static TElement MaxElement(this IEnumerable source, Func selector) where TData : IComparable
        {
            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.

提交回复
热议问题