10 Stimmen

Abrufen von Max() einer Count() mit LINQ

Ich bin neu bei LINQ und ich habe diese Situation. Ich habe diese Tabelle:

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

Ich möchte nur die maximale Anzahl von Zeilen pro Datum nach Bereich auflisten, etwa so:

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

Ich möchte dies tun, indem Sie LINQ, haben Sie irgendwelche Ideen, wie dies zu tun?

6voto

StriplingWarrior Punkte 141402

Ich denke, etwas in dieser Richtung sollte funktionieren:

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

5voto

Jla Punkte 11084

Verwendung von Erweiterungsmethoden:

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

1voto

Abe Miessler Punkte 78979

Leider kann ich das im Moment nicht testen, aber versuchen Sie es mal:

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

0voto

Ecyrb Punkte 2000

Dieser Ansatz:
1) Gruppen nach Datum
2) Gruppiert für jedes Datum nach Bereich und berechnet die Summe
3) Wählt für jedes Datum die Position mit der höchsten Gesamtsumme aus.
4) Sie haben Ihr Ergebnis erhalten

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

Laut LINQ in Action (Kapitel 5.3.3 - Will LINQ to Objects hurt the performance of my code?) ist die Verwendung der MaxElement-Erweiterungsmethode einer der effizientesten Ansätze. Ich denke, die Leistung wäre O(4n); eine für die erste GroupBy, zwei für die zweite GroupBy, drei für die Count() und vier für die Schleife innerhalb von MaxElement.

DrDro's Ansatz wird mehr wie O(n^2) sein, da es die gesamte Liste für jedes Element in der Liste schleift.

Der Ansatz von StriplingWarrior liegt näher an O(n log n), da er die Elemente sortiert. Aber ich gebe zu, dass da vielleicht ein bisschen verrückte Magie drinsteckt, die ich nicht verstehe.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X