Introduction
Aggregate LinQ operator is an unknown in the LinQs worlds. The goal of the project is review the LinQ Aggregators operators (Count
, LongCount
, Min
, Max
, Sum
and Average
), and show all operators can build 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 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 much bigger than Count
Operator 9.223.372.036.854.775.807
elements. The Count
operator should be sufficient for natural cases of day to day.
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 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, only change the datatype
.
LongCont
only should be used 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 that 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 this case, they are generics methods with two parameters. The first parameter is the same as that first group. The second selector
parameter equals to 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
Same formula and same behavior and treatment as for his brothers, 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 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("Initial 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: Dato
= (string name) and Len
= (length of dato).
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: