Aggregate Data in C# LINQ: Sum, Average, Min, Max
LINQ (Language Integrated Query) in C# provides powerful aggregation
operators that allow you to compute summary values from collections
without writing imperative loops. This guide explains how to calculate
totals with Sum, determine means with Average,
find extremes with Min and Max, and perform
complex custom rollups using Aggregate, accompanied by
practical, clear C# code examples.
Setup: Sample Data
The examples below use this collection of objects:
public record Product(string Name, decimal Price, int UnitsSold);
var products = new List<Product>
{
new("Laptop", 1200.00m, 5),
new("Mouse", 25.50m, 40),
new("Keyboard", 75.00m, 15),
new("Monitor", 300.00m, 8)
};Sum
The Sum method calculates the total of numeric values in
a sequence. You can sum primitive numeric collections directly or pass a
lambda expression to select a specific numeric property from
objects.
// Direct sum of a numeric collection
int[] numbers = { 10, 20, 30, 40 };
int totalNumber = numbers.Sum(); // Result: 100
// Sum with a selector on an object property
decimal totalInventoryPrice = products.Sum(p => p.Price); // Result: 1600.50
// Calculating derived totals
decimal totalRevenue = products.Sum(p => p.Price * p.UnitsSold); // Result: 9545.00Average
The Average method computes the arithmetic mean of a
sequence of numeric values. It supports types such as int,
long, float, double, and
decimal.
// Average of primitive values
double[] ratings = { 4.5, 3.8, 5.0, 4.2 };
double meanRating = ratings.Average(); // Result: 4.375
// Average with an object selector
decimal averagePrice = products.Average(p => p.Price); // Result: 400.125
// When applied to integers, Average returns double
int[] scores = { 1, 2, 4 };
double averageScore = scores.Average(); // Result: 2.3333333333333335Note: Calling Average on an empty collection throws
an InvalidOperationException.
Min and Max
Min and Max retrieve the smallest and
largest values in a collection, respectively. In modern .NET, LINQ also
supports MinBy and MaxBy to return the entire
object associated with the extreme value rather than just the numeric
property.
// Finding minimum and maximum scalar values
decimal lowestPrice = products.Min(p => p.Price); // Result: 25.50
int mostUnitsSold = products.Max(p => p.UnitsSold); // Result: 40
// MinBy and MaxBy return the actual object (available in .NET 6+)
Product? cheapestProduct = products.MinBy(p => p.Price);
// Result: Product { Name = "Mouse", Price = 25.50, UnitsSold = 40 }
Product? topSeller = products.MaxBy(p => p.UnitsSold);
// Result: Product { Name = "Mouse", Price = 25.50, UnitsSold = 40 }Aggregate
The Aggregate method applies an accumulator function
over a sequence. It is the most versatile LINQ aggregation operator,
serving as the foundation for fold/reduce operations.
1. Basic Accumulator
Operates directly on the elements of the collection:
string[] words = { "LINQ", "makes", "data", "aggregation", "easy" };
string sentence = words.Aggregate((current, next) => $"{current} {next}");
// Result: "LINQ makes data aggregation easy"2. Accumulator with a Seed Value
Allows you to supply an initial value, which can be of a different type than the collection elements:
// Seed with an initial account balance of $1000 and apply debits
decimal[] expenses = { 120.50m, 45.00m, 210.75m };
decimal startingBalance = 1000.00m;
decimal remainingBalance = expenses.Aggregate(
startingBalance,
(balance, expense) => balance - expense
);
// Result: 623.753. Accumulator with Seed and Result Selector
Transforms the final accumulated value into an alternative output format:
// Generate a comma-separated list of product names formatted in uppercase
string csvProductNames = products.Aggregate(
new StringBuilder(),
(sb, product) => sb.Append(product.Name).Append(", "),
sb => sb.ToString().TrimEnd(',', ' ').ToUpper()
);
// Result: "LAPTOP, MOUSE, KEYBOARD, MONITOR"