2009-06-09 37 views
19

Digamos que tengo esta clase amputada Person:C#: ¿Cómo utilizar el método Enumerable.Aggregate

class Person 
{ 
    public int Age { get; set; } 
    public string Country { get; set; } 

    public int SOReputation { get; set; } 
    public TimeSpan TimeSpentOnSO { get; set; } 

    ... 
} 

que pueda después agrupa Age y Country así:

var groups = aListOfPeople.GroupBy(x => new { x.Country, x.Age }); 

entonces puedo salida todos los grupos con su reputación total como esta:

foreach(var g in groups) 
    Console.WriteLine("{0}, {1}:{2}", 
     g.Key.Country, 
     g.Key.Age, 
     g.Sum(x => x.SOReputation)); 

Mi pregunta es, ¿cómo puedo obtener una suma de la propiedad TimeSpentOnSO? El método Sum no funcionará en este caso ya que solo es para int y tal. Pensé que podría usar el método Aggregate, pero realmente no puedo entender cómo usarlo ... Estoy probando todas las propiedades y tipos de clases en varias combinaciones, pero el compilador simplemente no lo reconocerá.

foreach(var g in groups) 
    Console.WriteLine("{0}, {1}:{2}", 
     g.Key.Country, 
     g.Key.Age, 
     g.Aggregate( what goes here?? )); 

¿He entendido por completo el método Aggregate? ¿O qué está pasando? ¿Es algún otro método que debería usar en su lugar? ¿O tengo que escribir mi propia variante Sum para TimeSpan s?

Y para agregar al desastre, ¿qué pasa si la persona es una clase anónima, como resultado de, por ejemplo, una declaración Select o GroupJoin?


Sólo di cuenta de que podía hacer el trabajo Aggregate método si lo hiciera un Select en la propiedad TimeSpan primera ... pero me parece que un poco molesto ... todavía no me siento que entiendo este método en todos los ...

foreach(var g in groups) 
    Console.WriteLine("{0}, {1}:{2}", 
     g.Key.Country, 
     g.Key.Age, 
     g.Select(x => x.TimeSpentOnSO) 
     g.Aggregate((sum, x) => sum + y)); 
+0

Solo quiero señalar que Aggregate (reducir) es una de las cosas más geniales del mundo ahora que me he encontrado situaciones donde quería usarlo :) –

Respuesta

2

Una combinación de Chris y Daniels respuestas resuelto por mí. Necesitaba inicializar TimeSpan, e hice las cosas en el orden incorrecto. La solución es:

foreach(var g in groups) 
    Console.WriteLine("{0}, {1}:{2}", 
     g.Key.Country, 
     g.Key.Age, 
     g.Aggregate(TimeSpan.Zero, (sum, x) => sum + x.TimeSpentOnSO)); 

Gracias!

Y también ... D'oh!

1

Se puede escribir TimeSpanSum método ...

public static TimeSpan Sum(this IEnumerable<TimeSpan> times) 
{ 
    return TimeSpan.FromTicks(times.Sum(t => t.Ticks)); 
} 
public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, 
    Func<TSource, TimeSpan> selector) 
{ 
    return TimeSpan.FromTicks(source.Sum(t => selector(t).Ticks)); 
} 

Alternativamente, MiscUtil tiene métodos Sum genéricos habilitados, por lo que Sum debería funcionar en un TimeSpan bien (ya que hay un operador TimeSpan+TimeSpan=>TimeSpan definido).

Sólo favor No me diga que el número ... me asustaría ...

+0

Sí, esa es una especie de ruta de respaldo. Pero aún me gustaría aprender cómo funciona realmente el método Aggregate. Porque me molesta que parezca que no puedo usarlo con éxito: p – Svish

+0

Y sí, vamos a mantener ese número secreto para siempre. Nunca pienses en publicar ningún tipo de estadísticas como esta aquí en SO: p (Aunque debo decir que mi curiosidad lo quiere ... pero manténlo en privado en ese caso!) – Svish

23
List<TimeSpan> list = new List<TimeSpan> 
    { 
     new TimeSpan(1), 
     new TimeSpan(2), 
     new TimeSpan(3) 
    }; 

TimeSpan total = list.Aggregate(TimeSpan.Zero, (sum, value) => sum.Add(value)); 

Debug.Assert(total.Ticks == 6); 
+0

Inicializa con TimeSpan.Zero. Muy buen punto. Sin embargo, esto fue con una lista de TimeSpans, necesitaba una lista de algo más con una propiedad TimeSpan. Pero lo descubrí. ¡Gracias! – Svish

14
g.Aggregate(TimeSpan.Zero, (i, p) => i + p.TimeSpentOnSO) 

Básicamente, el primer argumento es un inicializador agregado, que se utiliza como el primer valor de "i" en la función pasó en el segundo argumento. Se repetirá sobre la lista, y cada vez, "i" contendrá el total hasta ahora.

Por ejemplo:

List<int> nums = new List<int>{1,2,3,4,5}; 

nums.Aggregate(0, (x,y) => x + y); // sums up the numbers, starting with 0 => 15 
nums.Aggregate(0, (x,y) => x * y); // multiplies the numbers, starting with 0 => 0, because anything multiplied by 0 is 0 
nums.Aggregate(1, (x,y) => x * y); // multiplies the numbers, starting with 1 => 120 
+0

Esto fue con Ints, que ya había solucionado un poco. ¡Pero gracias por aclarar cómo inicializarlo! – Svish

+0

Lo siento, originalmente estaba usando Ints. Leí mal la pregunta, pero esto debería funcionar para usted, y también le explico cómo funciona Aggregate en general. –

0

Se podría resumir en una de las propiedades totales de la TimeSpan. Por ejemplo, usted podría conseguir los totalhours representados de tiempo dedicado a SO así:

g.Sum(x => x.SOReputation.TotalHours) 

creo que esto le daría el resultado que está buscando, pero con la advertencia de que habría que poner el unidades de medida según lo que necesite (horas, minutos, segundos, días, etc.)

+0

En realidad esto es lo que terminé haciendo, pero por otras razones. Todavía estoy feliz de que finalmente entiendo el método Agregado (aunque no es un experto: p). – Svish

Cuestiones relacionadas