BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

LINQ
.NET 3.5+

LINQ Aggregation

The seventh part of the LINQ to Objects tutorial looks at aggregate functions. These are standard query operators that can be applied to sequences and groups, performing a calculation that combines all of the values in a collection.

Aggregation Standard Query Operators

LINQ provides a number of aggregation operators. These are standard query operators, all extension methods of the IEnumerable<T> interface, that perform a calculation across all of the values in a collection. For example, you can use aggregate operators to count the number of items in a sequence or to sum a set of numeric values. All of the methods can be used to aggregate all of the items in a sequence, or with the GroupBy operator to calculate aggregate values for a number of groups.

To demonstrate the operators we need a class and some sample data. The class will hold the details of a stock item, including a name, category and price. The code is as follows:

public class StockItem
{
    public string Name { get; set; }
    public string Category { get; set; }
    public double Price { get; set; }

    public StockItem(string name, string category, double price)
    {
        Name = name;
        Category = category;
        Price = price;
    }
}

To create a list of sample stock items, add the following code:

var stock = new List<StockItem>
{
    new StockItem("Apple", "Fruit", 0.30),
    new StockItem("Banana", "Fruit", 0.35),
    new StockItem("Orange", "Fruit", 0.29),
    new StockItem("Cabbage", "Vegetable", 0.49),
    new StockItem("Carrot", "Vegetable", 0.29),
    new StockItem("Lettuce", "Vegetable", 0.30),
    new StockItem("Milk", "Dairy", 1.12)
};

Count

The first of the aggregation standard query operators that we will consider is Count. This simple method returns the number of items in a sequence, in a similar manner to the Count property supported by collections that implement the ICollection interface. The return value is an integer.

var count = stock.Count(); // count = 7

Aggregate operators are often used with grouped data. In the following example the stock items are grouped according to their categories. The results are returned as a set of anonymous types, each containing a category name and the number of items within that category:

var groupCounts = stock.GroupBy(
    s => s.Category, (c, i) => new { Category = c, Count = i.Count() });

/* RESULTS

{ Category = Fruit, Count = 3 }
{ Category = Vegetable, Count = 3 }
{ Category = Dairy, Count = 1 }

*/

The Count method has an overloaded version that accepts a Func delegate, usually a lambda expression, that acts as a predicate. This allows you to count the number of items that meet certain conditions. In the code below, the predicate returns true when the price of an item is greater than 0.3. This means that only stock items with a price of over 0.3 are counted for each category:

var groupCounts = stock.GroupBy(
    s => s.Category, (c, i) => new { Category = c, Count = i.Count(s => s.Price > 0.3) });

/* RESULTS

{ Category = Fruit, Count = 1 }
{ Category = Vegetable, Count = 1 }
{ Category = Dairy, Count = 1 }

*/

LongCount

The LongCount operator is almost identical to Count. It can be used for entire sequences or for groups, and can include a predicate that determines which items to count. The key difference is that the result is a long, rather than a standard integer.

var groupCounts = stock.GroupBy(s => s.Category,
    (c, i) => new { Category = c, Count = i.LongCount(s => s.Price > 0.3) });

/* RESULTS

{ Category = Fruit, Count = 1 }
{ Category = Vegetable, Count = 1 }
{ Category = Dairy, Count = 1 }

*/

Sum

The Sum method totals the values in a sequence. When used with a collection that contains only numeric values, the method can be used with no arguments. In the sample below, the values in an array of integers are summed:

var sum = new int[] { 1, 2, 3, 4 }.Sum(); // sum = 10

If you are working with non-numeric types, an overloaded version of Sum can be used. This overload accepts a single parameter containing a Func delegate that returns the values to sum, known as a selector function. This is demonstrated in the example below, which groups the stock items by category and sums the prices in each group. The lambda expression, "s => s.Price" is used to return each price.

var groupSums = stock.GroupBy(s => s.Category,
    (c, i) => new { Category = c, Sum = i.Sum(s => s.Price) });

/* RESULTS

{ Category = Fruit, Sum = 0.94 }
{ Category = Vegetable, Sum = 1.08 }
{ Category = Dairy, Sum = 1.12 }

*/

NB: if the sequence of values contains no items, the sum will be zero.

12 August 2010