Skip to main content

C# LINQ Aggregation

In many programming scenarios, you'll need to calculate summaries or statistics based on collections of data. LINQ provides powerful aggregation methods that allow you to perform these calculations efficiently and with minimal code. In this tutorial, you'll learn how to use LINQ's aggregation functions to transform collections into single values.

What is LINQ Aggregation?

LINQ aggregation refers to operations that process a collection of values to return a single result. These operations include functions like:

  • Calculating sums and averages
  • Finding minimum and maximum values
  • Counting elements that match a condition
  • Concatenating strings

Rather than writing manual loops with accumulators, LINQ provides simple, readable methods to achieve these tasks.

Common LINQ Aggregation Methods

Let's explore the most commonly used LINQ aggregation methods:

Count and LongCount

The Count() method returns the number of elements in a collection, optionally filtered by a condition.

csharp
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// Count all elements
int count = numbers.Count();
Console.WriteLine($"Total numbers: {count}");

// Count elements matching a condition
int evenCount = numbers.Count(n => n % 2 == 0);
Console.WriteLine($"Even numbers: {evenCount}");

Output:

Total numbers: 10
Even numbers: 5

For very large collections (more than int.MaxValue elements), you can use LongCount() which returns a long instead of an int.

Sum

The Sum() method calculates the sum of numeric values in a collection.

csharp
var numbers = new List<int> { 1, 2, 3, 4, 5 };

// Sum all values
int sum = numbers.Sum();
Console.WriteLine($"Sum of all numbers: {sum}");

// Sum with selector
var products = new List<Product>
{
new Product { Name = "Laptop", Price = 999.99m },
new Product { Name = "Phone", Price = 499.50m },
new Product { Name = "Headphones", Price = 149.99m }
};

decimal totalValue = products.Sum(p => p.Price);
Console.WriteLine($"Total product value: ${totalValue}");

Output:

Sum of all numbers: 15
Total product value: $1649.48

Average

The Average() method calculates the average of numeric values in a collection.

csharp
var scores = new[] { 78, 92, 65, 89, 76 };

// Calculate average
double average = scores.Average();
Console.WriteLine($"Average score: {average}");

// Average with selector
var employees = new List<Employee>
{
new Employee { Name = "Alice", Salary = 55000 },
new Employee { Name = "Bob", Salary = 62000 },
new Employee { Name = "Charlie", Salary = 48000 }
};

double averageSalary = employees.Average(e => e.Salary);
Console.WriteLine($"Average salary: ${averageSalary}");

Output:

Average score: 80
Average salary: $55000

Min and Max

The Min() and Max() methods find the minimum and maximum values in a collection.

csharp
var temperatures = new[] { 23.5, 31.2, 18.7, 27.9, 22.0 };

// Find min and max values
double lowest = temperatures.Min();
double highest = temperatures.Max();

Console.WriteLine($"Lowest temperature: {lowest}°C");
Console.WriteLine($"Highest temperature: {highest}°C");

// Min/Max with selector
var books = new List<Book>
{
new Book { Title = "C# in Depth", Pages = 528 },
new Book { Title = "LINQ Pocket Reference", Pages = 192 },
new Book { Title = "Programming C# 8.0", Pages = 784 }
};

int fewestPages = books.Min(b => b.Pages);
string longestBook = books.OrderByDescending(b => b.Pages).First().Title;

Console.WriteLine($"Fewest pages: {fewestPages}");
Console.WriteLine($"Longest book: {longestBook}");

Output:

Lowest temperature: 18.7°C
Highest temperature: 31.2°C
Fewest pages: 192
Longest book: Programming C# 8.0

Aggregate

The Aggregate() method is the most flexible aggregation method, allowing you to define a custom aggregation operation with an accumulator.

csharp
var words = new[] { "Hello", "LINQ", "Aggregation", "Examples" };

// Join words with commas
string combined = words.Aggregate((current, next) => current + ", " + next);
Console.WriteLine($"Combined words: {combined}");

// Using seed value (starting with an initial value)
string prefix = "Words: ";
string result = words.Aggregate(prefix, (current, next) => current + next + " ");
Console.WriteLine(result);

Output:

Combined words: Hello, LINQ, Aggregation, Examples
Words: Hello LINQ Aggregation Examples

A more complex example with a custom accumulator:

csharp
var numbers = new[] { 1, 2, 3, 4, 5 };

// Calculate factorial (n!)
int factorial = numbers.Aggregate(1, (product, factor) => product * factor);
Console.WriteLine($"5! = {factorial}");

// Compute running totals
var runningTotals = numbers.Aggregate(
new List<int>(),
(list, next) => {
int sum = list.Count > 0 ? list.Last() + next : next;
list.Add(sum);
return list;
});

Console.WriteLine($"Running totals: {string.Join(", ", runningTotals)}");

Output:

5! = 120
Running totals: 1, 3, 6, 10, 15

Error Handling in Aggregation

LINQ aggregation methods may throw exceptions if they're used on empty collections. Here's how to handle them:

csharp
var emptyList = new List<int>();

// These will throw InvalidOperationException for empty collections
try
{
// Console.WriteLine(emptyList.Average()); // Throws exception
// Console.WriteLine(emptyList.Min()); // Throws exception
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}

// Using nullable versions is safer
double? safeAverage = emptyList.DefaultIfEmpty().Average();
int? safeMin = emptyList.DefaultIfEmpty().Min();

Console.WriteLine($"Safe average: {safeAverage}");
Console.WriteLine($"Safe min: {safeMin}");

// Count is always safe
Console.WriteLine($"Count: {emptyList.Count()}");

// Sum is safe for empty collections of numbers
Console.WriteLine($"Sum: {emptyList.Sum()}");

Output:

Safe average: 0
Safe min: 0
Count: 0
Sum: 0

Real-World Examples

Example 1: Analyzing Sales Data

csharp
var sales = new List<SaleRecord>
{
new SaleRecord { Product = "Widget A", Revenue = 1200.50m, Month = "January" },
new SaleRecord { Product = "Widget B", Revenue = 875.25m, Month = "January" },
new SaleRecord { Product = "Widget A", Revenue = 1350.75m, Month = "February" },
new SaleRecord { Product = "Widget C", Revenue = 2100.00m, Month = "February" },
new SaleRecord { Product = "Widget B", Revenue = 900.50m, Month = "March" },
new SaleRecord { Product = "Widget C", Revenue = 2300.75m, Month = "March" }
};

// Total revenue
decimal totalRevenue = sales.Sum(s => s.Revenue);

// Best-selling product
var productSales = sales
.GroupBy(s => s.Product)
.Select(group => new {
Product = group.Key,
TotalRevenue = group.Sum(s => s.Revenue)
});

var bestProduct = productSales.OrderByDescending(p => p.TotalRevenue).First();
var avgRevenuePerProduct = productSales.Average(p => p.TotalRevenue);

// Monthly averages
var monthlyAverages = sales
.GroupBy(s => s.Month)
.Select(group => new {
Month = group.Key,
AverageRevenue = group.Average(s => s.Revenue)
});

Console.WriteLine($"Total revenue: ${totalRevenue}");
Console.WriteLine($"Best-selling product: {bestProduct.Product} (${bestProduct.TotalRevenue})");
Console.WriteLine($"Average revenue per product: ${avgRevenuePerProduct}");
Console.WriteLine("Monthly average revenues:");

foreach (var month in monthlyAverages)
{
Console.WriteLine($" {month.Month}: ${month.AverageRevenue}");
}

Output:

Total revenue: $8727.75
Best-selling product: Widget C ($4400.75)
Average revenue per product: $2909.25
Monthly average revenues:
January: $1037.875
February: $1725.375
March: $1600.625

Example 2: Document Analysis

csharp
string text = @"LINQ aggregation functions are powerful tools for data analysis. 
They help developers compute statistics from collections of data efficiently.
By using LINQ, C# developers can write cleaner, more readable code.";

// Word analysis
var words = text.Split(new[] { ' ', '\n', '\r', '.', ',' }, StringSplitOptions.RemoveEmptyEntries);

int wordCount = words.Count();
double avgWordLength = words.Average(w => w.Length);
string longestWord = words.OrderByDescending(w => w.Length).First();

// Character frequency
var characterFrequency = text.ToLower()
.Where(char.IsLetter)
.GroupBy(c => c)
.Select(g => new { Character = g.Key, Count = g.Count() })
.OrderByDescending(x => x.Count);

var mostCommonChar = characterFrequency.First();

Console.WriteLine($"Word count: {wordCount}");
Console.WriteLine($"Average word length: {avgWordLength:F1} characters");
Console.WriteLine($"Longest word: '{longestWord}' ({longestWord.Length} characters)");
Console.WriteLine($"Most common letter: '{mostCommonChar.Character}' (appears {mostCommonChar.Count} times)");

Output:

Word count: 26
Average word length: 5.5 characters
Longest word: 'efficiently' (11 characters)
Most common letter: 'e' (appears 18 times)

Summary

LINQ aggregation methods provide elegant solutions for calculating summaries and statistics from collections of data. They help you:

  • Calculate sums, averages, minimums, and maximums
  • Count elements matching specific conditions
  • Perform custom aggregate operations with accumulators
  • Avoid verbose looping code with concise, intention-revealing syntax

As you continue working with collections in C#, these aggregation methods will become essential tools in your programming toolbox, especially for data analysis, reporting, and business logic implementation.

Additional Resources

Exercises

  1. Calculate the total, average, minimum, and maximum price from a list of products.
  2. Find the most frequent word in a text, ignoring case and punctuation.
  3. Implement a custom aggregation that calculates the median value of a collection.
  4. Create a method that uses aggregation to build a comma-separated string from a collection, with "and" before the last item (e.g., "apple, banana, and orange").
  5. Write a LINQ query that calculates running statistics (min, max, average, count) for a sequence of numeric measurements.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)