Aggregate, The True LINQ Aggregator Operator

Aggregate LINQ operator is unknown in the LinQs world. The goal of the project is to review the LinQ Aggregators operators (Count, LongCount, Min, Max, Sum and Average), and show all operators can be built with the Aggregate operator.

Aggregate LINQ operator is unknown in the LINQ world. The goal of the project is reviewing the LINQ Aggregator operators (Count, LongCount, Min, Max, Sum, and Average), and showing how all operators can be built with the Aggregate operator. 

Index

  • Count and LongCount
  • Sum
  • Min y Max
  • Average
  • Aggregate
  • Previous operators Aggregate implementations
    • Sum
    • Min
    • Max
    • Average

Count and LongCount

The extension methods Count and LongCount, return the number of elements of the sequence. The difference between Count and LongCount is the return type. Count operator returns an int (System.Int32) type, and your limit is 2.147.483.647 elements. LongCount operator returns a Long (System.Int64) type, and your limit is  bigger than Count Operator 9.223.372.036.854.775.807 elements. The Count operator should be sufficient for day-to-day cases.

Signatures

  1. public static int Count<TSource>(this IEnumerable<TSource> source);  
  2. public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);  
  3.    
  4. public static long LongCount<TSource>(this IEnumerable<TSource> source);  
  5. public static long LongCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);   

Both of them have two overloads. The first overload is a simple method and counts all elements of a sequence. The second overload has a predicate parameter (Func<TSource, bool>) for counting only a part of the sequence.

Examples

  1. public static void CountAndLongCountTests()  
  2. {  
  3.     var elements = new List<Element>()  
  4.     {  
  5.         new Element{ ID = 0, Amount = 10000},  
  6.         new Element{ ID = 1, Amount =  1000},  
  7.         new Element{ ID = 2, Amount =  2000},  
  8.         new Element{ ID = 3, Amount =  3000}  
  9.     };  
  10.    
  11.    
  12.     var numSmall = elements.Count();  
  13.     var numBig   = elements.LongCount();  
  14.    
  15.     var numSmallFiltered = elements.    Count(e => e.Amount >= 2000);  
  16.     var numBigFiltered   = elements.LongCount(e => e.Amount >= 2000);  
  17.    
  18.    
  19.     Console.WriteLine($"{nameof(numSmallFiltered)} type: {numSmallFiltered.GetType().Name}");  
  20.     Console.WriteLine($"{nameof(numBigFiltered)}   type: {numBigFiltered.GetType()  .Name}");  
  21.    
  22.     Console.Read();  
  23. }  

Console result.

Output

The result is the same, the only change is the datatype.

LongCont only should use for very big collections.

Sum

The Sum extension method returns the sum of elements sequence. These elements can be null.

The Sum operator has 20 overloads in 2 groups.

The first group is formed for methods with only one parameter. This parameter is always numeric and is the same type of the source collection type and the return type.

Signatures group 1.

  1. public static long     Sum(this IEnumerable<long>     source);  
  2. public static long?    Sum(this IEnumerable<long?>    source);  
  3. public static float?   Sum(this IEnumerable<float?>   source);  
  4. public static double   Sum(this IEnumerable<double>   source);  
  5. public static double?  Sum(this IEnumerable<double?>  source);  
  6. public static decimal? Sum(this IEnumerable<decimal?> source);  
  7. public static decimal  Sum(this IEnumerable<decimal>  source);  
  8. public static float    Sum(this IEnumerable<float>    source);  
  9. public static int?     Sum(this IEnumerable<int?>     source);  
  10. public static int      Sum(this IEnumerable<int>      source);  

Examples

  1. public static void SumTests1()  
  2. {  
  3.     var elements = new List<Element>()  
  4.     {  
  5.         new Element{ ID = 0, Amount = 10000},  
  6.         new Element{ ID = 1, Amount =  1000},  
  7.         new Element{ ID = 2, Amount =  2000},  
  8.         new Element{ ID = 3, Amount =  3000}  
  9.     };  
  10.    
  11.     int[] numbers = new int[] { 1000, 2000, 3000 };  
  12.    
  13.    
  14.     int resultNumbers = numbers.Sum();  
  15.    
  16.     var sumAmount = elements.Select(a => a.Amount).Sum();  
  17.    
  18.     Console.WriteLine($"{nameof(resultNumbers)}   = {resultNumbers}");  
  19.     Console.WriteLine($"{nameof(sumAmount)}       = {sumAmount}");  
  20.    
  21.     Console.Read();  
  22. }  

Console result

Output

The calls do not contain a parameter because it is an extension method parameter.

The second group has 10 methods too, for these cases are generics methods with two parameters. The first parameter is the same as the first group. The second selector parameter equals the Select extension method.

Signatures group 2.

  1. public static float?   Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, float?>   selector);  
  2. public static double?  Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, double?>  selector);  
  3. public static decimal  Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal>  selector);  
  4. public static decimal? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal?> selector);  
  5. public static double   Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, double>   selector);  
  6. public static int?     Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int?>     selector);  
  7. public static long?    Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, long?>    selector);  
  8. public static float    Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, float>    selector);  
  9. public static long     Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, long>     selector);  
  10. public static int      Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int>      selector);  

Examples

  1. public static void SumTests2()  
  2. {  
  3.     var elements = new List<Element>()  
  4.     {  
  5.         new Element { ID = 0, Amount = 10000},  
  6.         new Element { ID = 1, Amount =  1000},  
  7.         new Element { ID = 2, Amount =  2000},  
  8.         new Element { ID = 3, Amount =  3000}  
  9.     };  
  10.    
  11.     var sumAmount = elements.Sum(a => a.Amount);  
  12.    
  13.     Console.WriteLine($"{nameof(sumAmount)} = {sumAmount}");  
  14.    
  15.     Console.Read();  
  16. }  

We add a Func<Element, int> parameter.

Console result

Output

For the null values, the process will understand, null = 0.

  1. public static void SumTestsNull()  
  2. {  
  3.     var elements = new List<Element>()  
  4.     {  
  5.         new Element { ID = 0, Amount = null},  
  6.         new Element { ID = 1, Amount = null},  
  7.         new Element { ID = 2, Amount = null},  
  8.         new Element { ID = 3, Amount = null}  
  9.     };  
  10.    
  11.     var sumAmount = elements.Sum(a => a.Amount);  
  12.    
  13.     Console.WriteLine($"{nameof(sumAmount)} = {sumAmount}");  
  14.    
  15.     Console.Read();  
  16. }  

Console result

Output

Min and Max

The Min and Max operators, have the same peculiarities as Sum operator, but with a different objective. Max gets the highest value in the sequence and Min gets the minimum value.

Signatures

  1. public static double?   Max(this IEnumerable<double?>  source);  
  2. public static double    Max(this IEnumerable<double>   source);  
  3. public static long?     Max(this IEnumerable<long?>    source);  
  4. public static long      Max(this IEnumerable<long>     source);  
  5. public static int?      Max(this IEnumerable<int?>     source);  
  6. public static float     Max(this IEnumerable<float>    source);  
  7. public static float?    Max(this IEnumerable<float?>   source);  
  8. public static int       Max(this IEnumerable<int>      source);  
  9. public decimal          Max(this IEnumerable<decimal>  source);  
  10. public decimal?         Max(this IEnumerable<decimal?> source);  
  11.    
  12. public static long?     Min(this IEnumerable<long?>    source);  
  13. public static int?      Min(this IEnumerable<int?>     source);  
  14. public static int       Min(this IEnumerable<int>      source);  
  15. public static float?    Min(this IEnumerable<float?>   source);  
  16. public static double    Min(this IEnumerable<double>   source);  
  17. public static double?   Min(this IEnumerable<double?>  source);  
  18. public static decimal   Min(this IEnumerable<decimal>  source);  
  19. public static decimal?  Min(this IEnumerable<decimal?> source);  
  20. public static long      Min(this IEnumerable<long>     source);  
  21. public static float     Min(this IEnumerable<float>    source);  
  22.    
  23.    
  24. public static int       Max<TSource>(this IEnumerable<TSource> source, Func<TSource, int>      selector);  
  25. public static int?      Max<TSource>(this IEnumerable<TSource> source, Func<TSource, int?>     selector);  
  26. public static long      Max<TSource>(this IEnumerable<TSource> source, Func<TSource, long>     selector);  
  27. public static long?     Max<TSource>(this IEnumerable<TSource> source, Func<TSource, long?>    selector);  
  28. public static float     Max<TSource>(this IEnumerable<TSource> source, Func<TSource, float>    selector);  
  29. public static float?    Max<TSource>(this IEnumerable<TSource> source, Func<TSource, float?>   selector);  
  30. public static double    Max<TSource>(this IEnumerable<TSource> source, Func<TSource, double>   selector);  
  31. public static double?   Max<TSource>(this IEnumerable<TSource> source, Func<TSource, double?>  selector);  
  32. public static decimal   Max<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal>  selector);  
  33. public static decimal?  Max<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal?> selector);  
  34.    
  35. public static long?     Min<TSource>(this IEnumerable<TSource> source, Func<TSource, long?>    selector);  
  36. public static decimal?  Min<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal?> selector);  
  37. public static decimal   Min<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal>  selector);  
  38. public static float     Min<TSource>(this IEnumerable<TSource> source, Func<TSource, float>    selector);  
  39. public static double?   Min<TSource>(this IEnumerable<TSource> source, Func<TSource, double?>  selector);  
  40. public static int?      Min<TSource>(this IEnumerable<TSource> source, Func<TSource, int?>     selector);  
  41. public static int       Min<TSource>(this IEnumerable<TSource> source, Func<TSource, int>      selector);  
  42. public static float?    Min<TSource>(this IEnumerable<TSource> source, Func<TSource, float?>   selector);  
  43. public static long      Min<TSource>(this IEnumerable<TSource> source, Func<TSource, long>     selector);  
  44. public static double    Min<TSource>(this IEnumerable<TSource> source, Func<TSource, double>   selector);  

Simple example

  1. public static void MinMaxTests()  
  2. {  
  3.     int[] numbers = new int[] { 1000, 2000, 3000 };  
  4.    
  5.     int maxNumbers = numbers.Max();  
  6.     int minNumbers = numbers.Min();   
  7.     Console.WriteLine($"{nameof(maxNumbers) } = {maxNumbers }");  
  8.     Console.WriteLine($"{nameof(minNumbers) } = {minNumbers }");  
  9.    
  10.     Console.Read();  
  11. }  

Result

Output

Complex example.

  1. public static void MinMaxTests2()  
  2. {  
  3.     var elements = new List<Element>()  
  4.     {  
  5.         new Element { ID = 0, Amount = 10000},  
  6.         new Element { ID = 1, Amount =  1000},  
  7.         new Element { ID = 2, Amount =  2000},  
  8.         new Element { ID = 3, Amount =  3000}  
  9.     };  
  10.    
  11.     var maxAmountForSelect = elements.Max(a => a.Amount);  
  12.     var minAmountForSelect = elements.Min(a => a.Amount);  
  13.    
  14.     var maxAmountForField = elements.Max(a => a.Amount);  
  15.     var minAmountForField = elements.Min(a => a.Amount);  
  16.    
  17.     Console.WriteLine($"{nameof(maxAmountForSelect)} = {maxAmountForSelect}");  
  18.     Console.WriteLine($"{nameof(minAmountForSelect)} = {minAmountForSelect}");  
  19.     Console.WriteLine($"{nameof(maxAmountForField) } = {maxAmountForField }");  
  20.     Console.WriteLine($"{nameof(minAmountForField) } = {minAmountForField }");  
  21.    
  22.     Console.Read();  
  23. }  

Result.

Output

Average

This is the same formula and same behavior and treatment as for Sum, Max and Min, but to find the average.

In this case the return types are always types with decimal parts as decimal or double.

Signatures.

  1. public static float    Average(this IEnumerable<float>    source);  
  2. public static double?  Average(this IEnumerable<long?>    source);  
  3. public static float?   Average(this IEnumerable<float?>   source);  
  4. public static double   Average(this IEnumerable<double>   source);  
  5. public static double   Average(this IEnumerable<int>      source);  
  6. public static decimal  Average(this IEnumerable<decimal>  source);  
  7. public static decimal? Average(this IEnumerable<decimal?> source);  
  8. public static double   Average(this IEnumerable<long>     source);  
  9. public static double?  Average(this IEnumerable<double?>  source);  
  10. public static double?  Average(this IEnumerable<int?>     source);  
  11.   
  12. public static decimal  Average<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal>  selector);  
  13. public static decimal? Average<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal?> selector);  
  14. public static double?  Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int?>     selector);  
  15. public static double   Average<TSource>(this IEnumerable<TSource> source, Func<TSource, long>     selector);  
  16. public static double?  Average<TSource>(this IEnumerable<TSource> source, Func<TSource, long?>    selector);  
  17. public static float    Average<TSource>(this IEnumerable<TSource> source, Func<TSource, float>    selector);  
  18. public static float?   Average<TSource>(this IEnumerable<TSource> source, Func<TSource, float?>   selector);  
  19. public static double   Average<TSource>(this IEnumerable<TSource> source, Func<TSource, double>   selector);  
  20. public static double?  Average<TSource>(this IEnumerable<TSource> source, Func<TSource, double?>  selector);  
  21. public static double   Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int>      selector);  

Simple example.

  1. public static void Average1()  
  2. {  
  3.     int[] numbers = new int[] { 1000, 2000, 3000 };   
  4.     var mediaNumbers = numbers.Average();   
  5.     Console.WriteLine($"{nameof(mediaNumbers) } = {mediaNumbers }");   
  6.     Console.Read();  

Result.

Output

Complex example.

  1. public static void Average2() {  
  2.     var elements = new List < Element > () {  
  3.         new Element {  
  4.             ID = 0, Amount = 10000  
  5.         },  
  6.         new Element {  
  7.             ID = 1, Amount = 1000  
  8.         },  
  9.         new Element {  
  10.             ID = 2, Amount = 2000  
  11.         },  
  12.         new Element {  
  13.             ID = 3, Amount = 3000  
  14.         }  
  15.     };  
  16.     var mediaForSelect = elements.Max(a => a.Amount);  
  17.     var mediaForField = elements.Max(a => a.Amount);  
  18.     Console.WriteLine($ "{nameof(mediaForSelect)} = {mediaForSelect}");  
  19.     Console.WriteLine($ "{nameof(mediaForField) } = {mediaForField}");  
  20.     Console.Read();  
  21. }  

Result.

Output

Aggregate

In a nutshell, the extension method Aggregate as is used for the accumulates function.

Aggregate has three signatures.

  1. public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func);  
  2. public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func);  
  3. public static TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector);  

First signature

This is the simplest signature. The Aggregate operator calls the delegate anonymous Func<TAccumulate, TSource, TAccumulate> func (accumulate function), foreach elements in a collection. 

  1. public static void Aggregate1()  
  2. {  
  3.     string[] names = { "Mike""Julie""John""Laura""Other name" };   
  4.     string result = names.Aggregate((resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next));   
  5.     /// This is the translate in simple bucle  
  6.     //string result = string.Empty;   
  7.     //foreach (var next in nombres)  
  8.     //{  
  9.     //    result += string.Format("\r\n{0:-16}", next);  
  10.     //}   
  11.     Console.WriteLine(result);   
  12.     Console.Read();  
  13. }  

Result

Output

Second signature

The second signature is the same as the first but the second signature has a new parameter of TSource type. This parameter shows the initial accumulate value.

Example

  1. public static void Aggregate2()  
  2. {  
  3.     string[] names = { "Mike""Julie""John""Laura""Other name" };  
  4.    
  5.     string result = names.Aggregate("Inital Date", (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next));  
  6.    
  7.     /// This is the translate in simple bucle  
  8.     //string result = "Initial Date";  
  9.    
  10.     //foreach (var next in nombres)  
  11.     //{  
  12.     //    result += string.Format("\r\n{0:-16}", next);  
  13.     //}   
  14.     Console.WriteLine(result);   
  15.     Console.Read();  
  16. }  

Result

Output

Third signature

The last signature has the same characteristics as the previous but adds a new parameter for configuring the output format.

Simple example

  1. public static void Aggregate3()  
  2. {  
  3.     string[] names = { "Mike""Julie""John""Laura""Other name" };   
  4.     string result = names.Aggregate("Inital Date", (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next));   
  5.     string resultado = names.Aggregate("Inital Date",  
  6.                                 (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next),  
  7.                                 a => $"{a} \r\n --> Total characters:  {a.Length} "  
  8.                                 );   
  9.     Console.WriteLine(resultado);   
  10.     Console.Read();  
  11. }  

Result

Output

Complex example

  1. public static void Aggregate4()  
  2. {  
  3.     string[] names = { "Mike""Julie""John""Laura""Other name" };   
  4.     var separador = new string[] { "\r\n" };   
  5.     var result = names.Aggregate("Inital Date",  
  6.                                             (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next),  
  7.                                             // We create an Anonimous type foreach element  
  8.                                             a => a.Split(separador, StringSplitOptions.None).Select(b => new { Dato = b, Len = b.Length })  
  9.                                             );   
  10.     result.ToList().ForEach(r => Console.WriteLine(r));   
  11.     Console.Read();  
  12. }  

Result

Output

It’s very important to see the objective of the last parameter. In this example, we split the name strings for creating a new anonymous object with two properties: Data = (string name) and Len = (length of data).

Recreate all operators only with Aggregate

Now, we will demonstrate that the Aggregate is the most important operator, and we will demonstrate that with this operator we can be creating all previous operators.

Sum

  1. public static void Sum_Aggregate()  
  2. {  
  3.    
  4.     int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };   
  5.     var sum = numbers.Aggregate((total, next) => total += next);   
  6.     Console.WriteLine $"The {nameof(sum)} value is {sum}");   
  7.     Console.Read();  
  8. }  

Result

Output

Min

  1. public static void Min_Aggregate()  
  2. {  
  3.    
  4.     int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };   
  5.     var min = numbers.Aggregate((total, next) => next < total ? next : total);   
  6.     Console.WriteLine($"The {nameof(min)} value is {min}");   
  7.     Console.Read();  
  8. }  

Result

Output

Max

  1. public static void Max_Aggregate()  
  2. {  
  3.    
  4.     int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };   
  5.     var max = numbers.Aggregate((total, next) => next > total ? next : total);   
  6.     Console.WriteLine($"The {nameof(max)} value is {max}");   
  7.     Console.Read();  
  8. }  

Result

Output

Average

  1. public static void Average_Aggregate()  
  2. {   
  3.     int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  
  4.    
  5.     var average = numbers.Aggregate(  
  6.                                 0,  
  7.                                 (total, next) => total += next,  
  8.                                 a => decimal.Parse(a.ToString()) / decimal.Parse(numbers.Count().ToString())  
  9.                             );  
  10.    
  11.     Console.WriteLine($"The {nameof(average)} value is {average}");  
  12.    
  13.     Console.Read();  
  14. }  

Result

Output