2010-11-16 12 views
8

Soy nuevo en LINQ y tengo esta situación. Tengo esta tabla:Cómo obtener el máximo() de un conteo() con 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 

sólo quiero a la lista del Maximun de filas por fecha por rango, así:

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

quiero hacer esto mediante el uso de LINQ, ¿tienes alguna idea de cómo hacer esto?

Respuesta

1

Lamentablemente no puedo probar esto en el momento pero le daría prueba:

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

¿Puede explicar qué es lo que debe hacer? Como Max devuelve un solo valor, no estoy seguro de cómo puede ayudar en esta cuestión. – Jla

6

creo que algo en este sentido debería funcionar:

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

Un par de errores tipográficos: 1) debería leer "grupo i por i.Fecha" no "grupo i por nuevo i.Fecha" y 2) falta una coma después de "Rango = máximo.Rango". Typos aparte, esto funciona. Me tomó un tiempo para envolver mi cabeza, pero funciona. :) – Ecyrb

+0

@Ecyrb: he corregido los errores tipográficos. Debo admitir que fue una consulta más complicada de lo que parecía en un principio. Me estremezco ante la idea de tratar de hacerlo con SQL directo. – StriplingWarrior

4

Uso de métodos de extensión:

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

Aprendí varias cosas de este ejemplo, ¡gracias! – bwperrin

0

Este enfoque:
1) Grupos por Fecha
2) para cada fecha, los grupos de rango y calcula el total
3) para cada fecha, se selecciona el elemento con el mayor total
4) Se termina con el resultado de su

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

De acuerdo con LINQ en acción (Capítulo 5.3.3 - ¿LINQ to Objects perjudicará el rendimiento de mi código?), usar el método de extensión MaxElement es uno de los enfoques más eficaces. Creo que el rendimiento sería O (4n); uno para el primer GroupBy, dos para el segundo GroupBy, tres para el Count() y cuatro para el bucle dentro de MaxElement.

El enfoque de DrDro va a ser más como O (n^2) ya que bucles toda la lista para cada elemento de la lista.

El enfoque de StriplingWarrior va a estar más cerca de O (n log n) porque ordena los elementos. Aunque lo admitiré, puede haber alguna magia loca que no entiendo.

+1

+1. Cuando se utiliza LINQ to Objects, este es un buen enfoque. Dado que OP mencionó una "tabla", asumo que está usando LINQ para Entidades o LINQ para SQL, que no admitirá sus métodos de extensión 'MaxElement' personalizados. En cuanto al rendimiento de mi respuesta, creo * que la combinación de 'OrderBy' y' FirstOrDefault' permitirá que el motor de la base de datos lo optimice y evite ordenar toda la tabla. – StriplingWarrior

Cuestiones relacionadas