LINQ - Aggregate
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);
Applies an accumulator function over a sequence.
The specified seed value is used as the initial
accumulator value, and the specified function is used to select the result
value.
Aggregate On String
IList<
String> strList =
new
List<
String>() {
"One",
"Two",
"Three",
"Four",
"Five"};
var commaSeperatedString = strList.Aggregate((s1, s2) =>
s1 +
", " + s2);
Console.WriteLine(commaSeperatedString);
Output:
One, Two, Three, Four, Five
Aggregate
with Seed Value C#
// Student collection
IList<
Student> studentList =
new List<
Student>>() {
new
Student() { StudentID = 1,
StudentName =
"John", Age = 13} ,
new
Student() { StudentID = 2,
StudentName =
"Moin", Age = 21 } ,
new
Student() { StudentID = 3,
StudentName =
"Bill", Age = 18 } ,
new
Student() { StudentID = 4,
StudentName =
"Ram" , Age = 20} ,
new
Student() { StudentID = 5,
StudentName =
"Ron" , Age = 15 }
};
string commaSeparatedStudentNames = studentList.Aggregate<
Student,
string>(
"Student Names: ",
// seed value
(str,
s) => str += s.StudentName +
"," );
Console.WriteLine(commaSeparatedStudentNames);
In the
above example, the first parameter of the Aggregate method is the "Student
Names: " string that will be accumulated with all student names. The comma
in the lambda expression will be passed as a second parameter.
Output :
Student Names: John,Moin,Bill,Ram,Ron,
Aggregate
with Result Selector C#
IList<
Student> studentList =
new List<
Student>>() {
new
Student() { StudentID = 1,
StudentName =
"John", Age = 13} ,
new
Student() { StudentID = 2,
StudentName =
"Moin", Age = 21 } ,
new
Student() { StudentID = 3,
StudentName =
"Bill", Age = 18 } ,
new
Student() { StudentID = 4,
StudentName =
"Ram" , Age = 20} ,
new
Student() { StudentID = 5,
StudentName =
"Ron" , Age = 15 }
};
string commaSeparatedStudentNames = studentList.Aggregate<
Student,
string,
string>(
String.Empty,
// seed value
(str, s) => str += s.StudentName +
",",
// returns result using seed value, String.Empty goes to
lambda expression as str
str =>
str.Substring(0,str.Length - 1 ));
// result selector that removes last comma
Console.WriteLine(commaSeparatedStudentNames);
Output :
John,Moin,Bill,Ram,Ron
Diagram
Explanation
Aggregate(<Func>)
Method
The Aggregate() method applies a function to all the elements of the
source sequence and calculates a cumulative result that takes into account the
return value of each function call.
This is a somewhat
complicated concept, and is best shown with some examples.
IEnumerable<int> ints =
new List<int> { 2, 4, 1, 6 };
//
Reimplementation of the Sum() method utilizing Aggregate()
// Will
return 13
int result = ints.Aggregate((sum, val) => sum +
val);
What's happening in
this example? We have provided the lambda expression (sum, val) => sum + val to the Aggregate() method. This
expression will be executed 3 (length of the list - 1) times:
It is passed 2 and
4 for the sum and val parameters,
and returns the sum: 6.
It is passed 6 and
1 for sum and val, and returns 7.
It is passed 7 and
6 and returns 13.
IEnumerable<string> strings = new List<string> { "a", "ab", "abc", "abcd", "z" };
// Will
return "a&ab&abc&abcd&z"
string result = strings.Aggregate((concat, str) => $"{concat}&{str}");
In this example,
the expression (concat,
str) => $"{concat}&{str}" is called 4 times:
It is passed
"a" and "ab" for the concat and str parameters,
and returns "a&ab".
It is passed
"a&ab" and "abc" and returns
"a&ab&abc".
"a&ab&abc"
and "abcd" → "a&ab&abc&abcd"
"a&ab&abc&abcd"
and "z" → "a&ab&abc&abcd&z"
NOTE: The return value of the Aggregate() call (and the provided func) must be the same data type as that of the elements in the
source sequence.
Aggregate(<Seed>,
<Func>)
Another form
of Aggregate() takes a seed,
which specifies the initial value supplied to the func. In addition, this form of Aggregate() can return a value of a different data type than that of
the elements in the source sequence. In this case, the data type of the value
returned from func must
match the data type of seed.
IEnumerable<string> strings = new List<string> { "a", "ab", "abc", "abcd" };
//
Reimplementation of the Count() method utilizing Aggregate()
// Will
return 4
int result = strings.Aggregate(0, (count,
val) => count + 1);
In this example,
the expression (count,
val) => count + 1 is called 4
times:
It is passed 0 and
"a" for the count and val parameters,
and returns 1.
It is passed 1 and
"ab" and returns 2.
2 and
"abc" → 3
3 and "abcd"
→ 4
IEnumerable<string> strings = new List<string> { "a", "ab", "abc", "abcd", "z" };
//
Reimplementation of Any(str => str.Length > 3) utilizing Aggregate()
// Will
return true
bool result = strings.Aggregate(false, (any, val) => any || (val.Length > 3));
In this example,
the expression (any, val)
=> any || (val.Length > 3) is called 5
times:
It is passed false and "a" for the any and val parameters,
and returns false.
It is passed false and "ab" and returns false.
false and "abc" → false
false and "abcd" → true
true and "z" → true
Aggregate
Implementation
This is .NET Framework
implementation of Enumerable.Aggregate method with only one
paramater func.
public static TSource Aggregate<TSource>(
this IEnumerable<TSource>
source,
Func<TSource, TSource,
TSource> func)
{
if (source
== null) throw Error.ArgumentNull("source");
if (func ==
null) throw Error.ArgumentNull("func");
using (IEnumerator<TSource>
e = source.GetEnumerator())
{
if
(!e.MoveNext()) throw Error.NoElements();
TSource
result = e.Current;
while
(e.MoveNext()) {
result = func(result, e.Current);
}
return
result;
}
}
This is .NET Framework
implementation of Enumerable.Aggregate method with three
parameters seed, func and resultSelector.
public static TResult Aggregate<TSource,
TAccumulate, TResult>(
this IEnumerable<TSource>
source,
TAccumulate
seed,
Func<TAccumulate, TSource,
TAccumulate> func,
Func<TAccumulate, TResult>
resultSelector)
{
if (source
== null) throw Error.ArgumentNull("source");
if (func ==
null) throw Error.ArgumentNull("func");
if
(resultSelector == null) throw Error.ArgumentNull("resultSelector");
TAccumulate
result = seed;
foreach (TSource element in
source) {
result = func(result, element);
}
return resultSelector(result);
}
Example 1
Enumerable.Aggregate
Method
Applies an accumulator function over a
sequence. The specified seed value is used as the initial accumulator value,
and the specified function is used to select the result value.
string[] fruits = { "apple", "mango",
"orange", "passionfruit", "grape"
};
// Determine
whether any string in the array is longer than "banana".
string longestName =
fruits.Aggregate("banana",
(longest, next) =>
next.Length >
longest.Length ? next : longest,
// Return the final result as an upper case string.
fruit =>
fruit.ToUpper());
Console.WriteLine(
"The fruit with the longest name is {0}.",
longestName);
// This code
produces the following output:
//
// The fruit with
the longest name is PASSIONFRUIT.
Example 2
int[] ints = { 4, 8, 8, 3, 9, 0, 7, 8, 2 };
// Count the even
numbers in the array, using a seed value of 0.
int numEven = ints.Aggregate(0, (total, next) =>
next % 2 == 0 ? total + 1
: total);
Console.WriteLine("The number of even integers is: {0}", numEven);
// This code
produces the following output:
//
// The number of
even integers is: 6
Example 3
string sentence = "the quick brown fox jumps over the lazy dog";
// Split the
string into individual words.
string[] words = sentence.Split(' ');
// Prepend each
word to the beginning of the
// new sentence to
reverse the word order.
string reversed =
words.Aggregate((workingSentence, next) =>
next + " " + workingSentence);
Console.WriteLine(reversed);
// This code
produces the following output:
//
// dog lazy the
over jumps fox brown quick the
Example 4
Summing numbers
var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a
+ b);
Console.WriteLine(sum); // output: 10
(1+2+3+4)
var numbers = new List<int>
{ 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: (result, item)
=> result + item);
// sum: (((6+2)+8)+3) = 19
Example 5
Method Add Insted Of Lambda Expression.
var
numbers = new List<int> { 6, 2, 8, 3 };
int
sum = numbers.Aggregate(func: Add);
// sum: (((6+2)+8)+3) = 19
private static int
Add(int
x, int y) { return
x + y; }
var
numbers = new List<int> { 6, 2, 8, 3 };
decimal average = numbers.Aggregate(
seed: 0,
func: (result,
item) => result + item,
resultSelector:
result => (decimal)result / numbers.Count);
// average: ((((0+6)+2)+8)+3) / 4 = 4.75
Example 6
Three Arguments Overload
Example
int[] books = { 100, 200, 300
};
var total = books.Aggregate(0,
(r, i) => r + i, r => r/(books.Length));
Console.WriteLine($"Avarage
book has {total} pages.");
Example 7
Using
LINQ Aggregate effectively
Using a loop
static Rectangle GetRectUnionLoop(params Rectangle[] rectangles)
{
if (rectangles.Length == 0)
return Rectangle.Empty;
Rectangle boundingBox = rectangles[0];
foreach (var rectangle in rectangles)
{
boundingBox
= Rectangle.Union(boundingBox, rectangle);
}
return boundingBox;
}
Using a
recursive function
static Rectangle GetRectUnionRecursive(params Rectangle[] rectangles)
{
return (rectangles.Length > 1)
? Rectangle.Union(rectangles[0],
GetRectUnionRecursive(rectangles.Skip(1).ToArray()))
: (rectangles.Length == 1)
? rectangles[0]
: Rectangle.Empty;
}
Using LINQ
static Rectangle GetRectUnionLINQ(params Rectangle[] rectangles)
{
return (rectangles.Length == 0)
? Rectangle.Empty
: rectangles
.Aggregate(rectangles[0], (acc, rect) => Rectangle.Union(acc, rect));
}
Example 8
Create A Csv From An Array Of Strings
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate(new StringBuilder(), (a,b) => {
if(a.Length>0)
a.Append(",");
a.Append(b);
return a;
});
Console.WriteLine(csv);
Example 9
Multiplying Numbers Using A Seed
For completeness, there is an overload of Aggregate
which
takes a seed value.
var multipliers = new []{10,20,30,40};
var multiplied = multipliers.Aggregate(5, (a,b) => a * b);
Console.WriteLine(multiplied); //Output 1200000
((((5*10)*20)*30)*40)
Example 10
Aggregate Used To Sum Columns In A Multi Dimensional Integer
Array
int[][] nonMagicSquare =
{
new int[] { 3, 1, 7, 8 },
new int[] { 2, 4, 16, 5 },
new int[] { 11, 6, 12, 15 },
new int[] { 9, 13, 10, 14 }
};
IEnumerable<int> rowSums = nonMagicSquare
.Select(row => row.Sum());
IEnumerable<int> colSums = nonMagicSquare
.Aggregate(
(priorSums, currentRow) =>
priorSums.Select((priorSum, index) => priorSum + currentRow[index]).ToArray()
);
Console.WriteLine("rowSums: " + string.Join(", ", rowSums)); // rowSums: 19, 27, 44, 46
Console.WriteLine("colSums: " + string.Join(", ", colSums)); // colSums: 25, 24, 45, 42
Example 11
bool[][] booleanTable =
{
new bool[] { true, true, true, false },
new bool[] { false, false, false, true },
new bool[] { true, false, false, true },
new bool[] { true, true, false, false }
};
IEnumerable<int> rowCounts = booleanTable
.Select(row => row.Select(value => value ? 1 : 0).Sum());
IEnumerable<int> seed = new int[booleanTable.First().Length];
IEnumerable<int> colCounts = booleanTable
.Aggregate(seed,
(priorSums, currentRow) =>
priorSums.Select((priorSum, index) => priorSum + (currentRow[index] ? 1 : 0)).ToArray()
);
Console.WriteLine("rowCounts: " + string.Join(", ", rowCounts)); // rowCounts: 3, 1, 2, 2
Console.WriteLine("colCounts: " + string.Join(", ", colCounts)); // colCounts: 3, 2, 1, 2