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
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
- public static int Count<TSource>(this IEnumerable<TSource> source);
- public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
-
- public static long LongCount<TSource>(this IEnumerable<TSource> source);
- 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
- public static void CountAndLongCountTests()
- {
- var elements = new List<Element>()
- {
- new Element{ ID = 0, Amount = 10000},
- new Element{ ID = 1, Amount = 1000},
- new Element{ ID = 2, Amount = 2000},
- new Element{ ID = 3, Amount = 3000}
- };
-
-
- var numSmall = elements.Count();
- var numBig = elements.LongCount();
-
- var numSmallFiltered = elements. Count(e => e.Amount >= 2000);
- var numBigFiltered = elements.LongCount(e => e.Amount >= 2000);
-
-
- Console.WriteLine($"{nameof(numSmallFiltered)} type: {numSmallFiltered.GetType().Name}");
- Console.WriteLine($"{nameof(numBigFiltered)} type: {numBigFiltered.GetType() .Name}");
-
- Console.Read();
- }
Console result.
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.
- public static long Sum(this IEnumerable<long> source);
- public static long? Sum(this IEnumerable<long?> source);
- public static float? Sum(this IEnumerable<float?> source);
- public static double Sum(this IEnumerable<double> source);
- public static double? Sum(this IEnumerable<double?> source);
- public static decimal? Sum(this IEnumerable<decimal?> source);
- public static decimal Sum(this IEnumerable<decimal> source);
- public static float Sum(this IEnumerable<float> source);
- public static int? Sum(this IEnumerable<int?> source);
- public static int Sum(this IEnumerable<int> source);
Examples
- public static void SumTests1()
- {
- var elements = new List<Element>()
- {
- new Element{ ID = 0, Amount = 10000},
- new Element{ ID = 1, Amount = 1000},
- new Element{ ID = 2, Amount = 2000},
- new Element{ ID = 3, Amount = 3000}
- };
-
- int[] numbers = new int[] { 1000, 2000, 3000 };
-
-
- int resultNumbers = numbers.Sum();
-
- var sumAmount = elements.Select(a => a.Amount).Sum();
-
- Console.WriteLine($"{nameof(resultNumbers)} = {resultNumbers}");
- Console.WriteLine($"{nameof(sumAmount)} = {sumAmount}");
-
- Console.Read();
- }
Console result
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.
- public static float? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, float?> selector);
- public static double? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, double?> selector);
- public static decimal Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal> selector);
- public static decimal? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal?> selector);
- public static double Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector);
- public static int? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int?> selector);
- public static long? Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, long?> selector);
- public static float Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, float> selector);
- public static long Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, long> selector);
- public static int Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector);
Examples
- public static void SumTests2()
- {
- var elements = new List<Element>()
- {
- new Element { ID = 0, Amount = 10000},
- new Element { ID = 1, Amount = 1000},
- new Element { ID = 2, Amount = 2000},
- new Element { ID = 3, Amount = 3000}
- };
-
- var sumAmount = elements.Sum(a => a.Amount);
-
- Console.WriteLine($"{nameof(sumAmount)} = {sumAmount}");
-
- Console.Read();
- }
We add a Func<Element, int> parameter.
Console result
For the null values, the process will understand, null = 0.
- public static void SumTestsNull()
- {
- var elements = new List<Element>()
- {
- new Element { ID = 0, Amount = null},
- new Element { ID = 1, Amount = null},
- new Element { ID = 2, Amount = null},
- new Element { ID = 3, Amount = null}
- };
-
- var sumAmount = elements.Sum(a => a.Amount);
-
- Console.WriteLine($"{nameof(sumAmount)} = {sumAmount}");
-
- Console.Read();
- }
Console result
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
- public static double? Max(this IEnumerable<double?> source);
- public static double Max(this IEnumerable<double> source);
- public static long? Max(this IEnumerable<long?> source);
- public static long Max(this IEnumerable<long> source);
- public static int? Max(this IEnumerable<int?> source);
- public static float Max(this IEnumerable<float> source);
- public static float? Max(this IEnumerable<float?> source);
- public static int Max(this IEnumerable<int> source);
- public decimal Max(this IEnumerable<decimal> source);
- public decimal? Max(this IEnumerable<decimal?> source);
-
- public static long? Min(this IEnumerable<long?> source);
- public static int? Min(this IEnumerable<int?> source);
- public static int Min(this IEnumerable<int> source);
- public static float? Min(this IEnumerable<float?> source);
- public static double Min(this IEnumerable<double> source);
- public static double? Min(this IEnumerable<double?> source);
- public static decimal Min(this IEnumerable<decimal> source);
- public static decimal? Min(this IEnumerable<decimal?> source);
- public static long Min(this IEnumerable<long> source);
- public static float Min(this IEnumerable<float> source);
-
-
- public static int Max<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector);
- public static int? Max<TSource>(this IEnumerable<TSource> source, Func<TSource, int?> selector);
- public static long Max<TSource>(this IEnumerable<TSource> source, Func<TSource, long> selector);
- public static long? Max<TSource>(this IEnumerable<TSource> source, Func<TSource, long?> selector);
- public static float Max<TSource>(this IEnumerable<TSource> source, Func<TSource, float> selector);
- public static float? Max<TSource>(this IEnumerable<TSource> source, Func<TSource, float?> selector);
- public static double Max<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector);
- public static double? Max<TSource>(this IEnumerable<TSource> source, Func<TSource, double?> selector);
- public static decimal Max<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal> selector);
- public static decimal? Max<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal?> selector);
-
- public static long? Min<TSource>(this IEnumerable<TSource> source, Func<TSource, long?> selector);
- public static decimal? Min<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal?> selector);
- public static decimal Min<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal> selector);
- public static float Min<TSource>(this IEnumerable<TSource> source, Func<TSource, float> selector);
- public static double? Min<TSource>(this IEnumerable<TSource> source, Func<TSource, double?> selector);
- public static int? Min<TSource>(this IEnumerable<TSource> source, Func<TSource, int?> selector);
- public static int Min<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector);
- public static float? Min<TSource>(this IEnumerable<TSource> source, Func<TSource, float?> selector);
- public static long Min<TSource>(this IEnumerable<TSource> source, Func<TSource, long> selector);
- public static double Min<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector);
Simple example
- public static void MinMaxTests()
- {
- int[] numbers = new int[] { 1000, 2000, 3000 };
-
- int maxNumbers = numbers.Max();
- int minNumbers = numbers.Min();
- Console.WriteLine($"{nameof(maxNumbers) } = {maxNumbers }");
- Console.WriteLine($"{nameof(minNumbers) } = {minNumbers }");
-
- Console.Read();
- }
Result
Complex example.
- public static void MinMaxTests2()
- {
- var elements = new List<Element>()
- {
- new Element { ID = 0, Amount = 10000},
- new Element { ID = 1, Amount = 1000},
- new Element { ID = 2, Amount = 2000},
- new Element { ID = 3, Amount = 3000}
- };
-
- var maxAmountForSelect = elements.Max(a => a.Amount);
- var minAmountForSelect = elements.Min(a => a.Amount);
-
- var maxAmountForField = elements.Max(a => a.Amount);
- var minAmountForField = elements.Min(a => a.Amount);
-
- Console.WriteLine($"{nameof(maxAmountForSelect)} = {maxAmountForSelect}");
- Console.WriteLine($"{nameof(minAmountForSelect)} = {minAmountForSelect}");
- Console.WriteLine($"{nameof(maxAmountForField) } = {maxAmountForField }");
- Console.WriteLine($"{nameof(minAmountForField) } = {minAmountForField }");
-
- Console.Read();
- }
Result.
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.
- public static float Average(this IEnumerable<float> source);
- public static double? Average(this IEnumerable<long?> source);
- public static float? Average(this IEnumerable<float?> source);
- public static double Average(this IEnumerable<double> source);
- public static double Average(this IEnumerable<int> source);
- public static decimal Average(this IEnumerable<decimal> source);
- public static decimal? Average(this IEnumerable<decimal?> source);
- public static double Average(this IEnumerable<long> source);
- public static double? Average(this IEnumerable<double?> source);
- public static double? Average(this IEnumerable<int?> source);
-
- public static decimal Average<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal> selector);
- public static decimal? Average<TSource>(this IEnumerable<TSource> source, Func<TSource, decimal?> selector);
- public static double? Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int?> selector);
- public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, long> selector);
- public static double? Average<TSource>(this IEnumerable<TSource> source, Func<TSource, long?> selector);
- public static float Average<TSource>(this IEnumerable<TSource> source, Func<TSource, float> selector);
- public static float? Average<TSource>(this IEnumerable<TSource> source, Func<TSource, float?> selector);
- public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector);
- public static double? Average<TSource>(this IEnumerable<TSource> source, Func<TSource, double?> selector);
- public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector);
Simple example.
- public static void Average1()
- {
- int[] numbers = new int[] { 1000, 2000, 3000 };
- var mediaNumbers = numbers.Average();
- Console.WriteLine($"{nameof(mediaNumbers) } = {mediaNumbers }");
- Console.Read();
- }
Result.
Complex example.
- public static void Average2() {
- var elements = new List < Element > () {
- new Element {
- ID = 0, Amount = 10000
- },
- new Element {
- ID = 1, Amount = 1000
- },
- new Element {
- ID = 2, Amount = 2000
- },
- new Element {
- ID = 3, Amount = 3000
- }
- };
- var mediaForSelect = elements.Max(a => a.Amount);
- var mediaForField = elements.Max(a => a.Amount);
- Console.WriteLine($ "{nameof(mediaForSelect)} = {mediaForSelect}");
- Console.WriteLine($ "{nameof(mediaForField) } = {mediaForField}");
- Console.Read();
- }
Result.
Aggregate
In a nutshell, the extension method Aggregate as is used for the accumulates function.
Aggregate has three signatures.
- public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func);
- public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func);
- 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.
- public static void Aggregate1()
- {
- string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
- string result = names.Aggregate((resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next));
-
-
-
-
-
-
- Console.WriteLine(result);
- Console.Read();
- }
Result
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
- public static void Aggregate2()
- {
- string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
-
- string result = names.Aggregate("Inital Date", (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next));
-
-
-
-
-
-
-
-
- Console.WriteLine(result);
- Console.Read();
- }
Result
Third signature
The last signature has the same characteristics as the previous but adds a new parameter for configuring the output format.
Simple example
- public static void Aggregate3()
- {
- string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
- string result = names.Aggregate("Inital Date", (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next));
- string resultado = names.Aggregate("Inital Date",
- (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next),
- a => $"{a} \r\n --> Total characters: {a.Length} "
- );
- Console.WriteLine(resultado);
- Console.Read();
- }
Result
Complex example
- public static void Aggregate4()
- {
- string[] names = { "Mike", "Julie", "John", "Laura", "Other name" };
- var separador = new string[] { "\r\n" };
- var result = names.Aggregate("Inital Date",
- (resultAcum, next) => resultAcum += string.Format("\r\n{0:-16}", next),
-
- a => a.Split(separador, StringSplitOptions.None).Select(b => new { Dato = b, Len = b.Length })
- );
- result.ToList().ForEach(r => Console.WriteLine(r));
- Console.Read();
- }
Result
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
- public static void Sum_Aggregate()
- {
-
- int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
- var sum = numbers.Aggregate((total, next) => total += next);
- Console.WriteLine $"The {nameof(sum)} value is {sum}");
- Console.Read();
- }
Result
Min
- public static void Min_Aggregate()
- {
-
- int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
- var min = numbers.Aggregate((total, next) => next < total ? next : total);
- Console.WriteLine($"The {nameof(min)} value is {min}");
- Console.Read();
- }
Result
Max
- public static void Max_Aggregate()
- {
-
- int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
- var max = numbers.Aggregate((total, next) => next > total ? next : total);
- Console.WriteLine($"The {nameof(max)} value is {max}");
- Console.Read();
- }
Result
Average
- public static void Average_Aggregate()
- {
- int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
-
- var average = numbers.Aggregate(
- 0,
- (total, next) => total += next,
- a => decimal.Parse(a.ToString()) / decimal.Parse(numbers.Count().ToString())
- );
-
- Console.WriteLine($"The {nameof(average)} value is {average}");
-
- Console.Read();
- }
Result