Monthly Archive for February, 2009

Page 2 of 2

System.Linq.Enumerable.Aggregate – Better Know an Extension Method Part 1

As apart of this series I will be investigating the Extension methods made available in the System.Linq namespace specifically for classes that implement IEnumerable. If you have any gems you have created for the extension method in question drop a comment below as I would love to see what people are coming up with.

The first extension method investigated is Aggregate the function documented as:

“Applies an accumulator function over a sequence.”

The following code is the simple program and data class I will be using to demonstrate the power of Aggregate! Please note if you are follow along line 19 is were the test cases are placed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Entry
{
    public int Id { get; set; }
    public string Name { get; set; }
}
 
class Program
{
    static void Main(string[] args)
    {
        var items = new List<Entry>()
        {
            new Entry() {Id = 1, Name = "Horse"},
            new Entry() {Id = 2, Name = "Dog"},
            new Entry() {Id = 3, Name = "Cat"},
            new Entry() {Id = 4, Name = "Pig"}
        };
 
        // This is were our test cases go!
 
        Console.ReadLine();
    }
}

Test case 1: Aggregate<(Of <(TSource>)>)(IEnumerable<(Of <(TSource>)>), Func<(Of <(TSource, TSource, TSource>)>))

First of all TSource is the strongly typed class the Enumeration contains so if your using List<string> your TSource is a string or if your using a List<MyObject> your TSource is a MyObject. The second important part is Func<(Of <(TSource, TSource, TSource>)> this says that aggregate will take a delegate that provides two TSources and returns one TSource following is an example of such a function:

Func<string, string, string> stupidFunction = (a, b) => b;

This really simple example takes two strings and returns one. Now we will look at the simplest of all 3 overloads available the only requirement is a lambda expression that returns the same type as the strongly typed Enumeration provides identified as TSource in the method signature.

19
20
21
22
23
24
25
var entry = items.Aggregate((a, b) =>
{
    a.Name += b.Name;
    return a;
});
Console.WriteLine(entry.Name);
Console.WriteLine(items[0].Name);

HorseDogCatPig
HorseDogCatPig

As you can see we have modified the Entry with the Id of 1 to contain an Aggregate of all the names, to avoid changing the first Entry we must introduce the idea of a Seed.

Test case 2: Aggregate<(Of <(TSource, TAccumulate>)>)(IEnumerable<(Of <(TSource>)>), TAccumulate, Func<(Of <(TAccumulate, TSource, TAccumulate>)>))

The seed we pass in will be the object we recieve as the result of calling Aggregate the same instance is passed into every call to the expression. The following uses the aggregate without modifying the Enumeration.

19
20
21
22
23
24
25
var entry = items.Aggregate(new Entry(), (a, b) => 
{
    a.Name += b.Name;
    return a;
});
Console.WriteLine(entry.Name);
Console.WriteLine(items[0].Name);

HorseDogCatPig
Horse

Another neat trick to try with Aggregate is to pass in a different kind of seed like. While the change is very suttle it has the effect of changing the return type to a StringBuilder and no longer an Entry so we must now call ToString instead of Name.

19
20
21
var entry = items.Aggregate(new StringBuilder(), (a, b) => a.Append(b.Name) );
Console.WriteLine(entry.ToString());
Console.WriteLine(items[0].Name);

HorseDogCatPig
Horse

Test case 3: Aggregate<(Of <(TSource, TAccumulate, TResult>)>)(IEnumerable<(Of <(TSource>)>), TAccumulate, Func<(Of <(TAccumulate, TSource, TAccumulate>)>), Func<(Of <(TAccumulate, TResult>)>))

The last overload gives you one more expression to play with which is run once all the items have been enumerated over, how you want to use this will depend on what your doing but the following just appends “. DONE!” to the end of the StringBuilder.

19
20
21
var entry = items.Aggregate(new StringBuilder(), (a, b) => a.Append(b.Name), x => x.Append(". DONE!"));
Console.WriteLine(entry.ToString());
Console.WriteLine(items[0].Name);

HorseDogCatPig. DONE!
Horse

Test case 4: Real life scenario
Goal: Add up all the orders and provide a rounded total.

class Order
{
    public double Total { get; set; }
}
 
class Program
{
    static void Main(string[] args)
    {
        var orders = new List<Order>()
         {
             new Order() {Total = 123.23},
             new Order() {Total = 512},
             new Order() {Total = 123.23},
             new Order() {Total = 5172.34},
             new Order() {Total = 647.518},
             new Order() {Total = 368.180},
             new Order() {Total = 12.84},
             new Order() {Total = 947.25},
         };
 
        var roundedGrandTotal = orders.Aggregate(new double(), 
            (total, order) => total += order.Total,
            (total) => Math.Round(total, 2));
 
        Console.WriteLine("{0:c}",roundedGrandTotal);
        Console.ReadLine();
    }
}

Result

$7,906.59

And thats it! I hope I have shed light on this extremely useful extension method which can be used for anything you dream up. This is my first article on .NET for this blog so please leave some feedback. System.Linq.Enumerable.All- Better Know an Extension Method Part 2

Testing WP-Syntax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public static class StylesheetExtensions
{
    public static string Stylesheet(this HtmlHelper helper, string file)
    {
        return Stylesheet(helper, new string[] { file }, null);
    }
 
    public static string Stylesheet(this HtmlHelper helper, string file, object htmlAttributes)
    {
        return Stylesheet(helper, new string[] { file }, new RouteValueDictionary(htmlAttributes));
    }
 
    public static string Stylesheet(this HtmlHelper helper, string file, RouteValueDictionary htmlAttributes)
    {
        return Stylesheet(helper, new string[] { file }, htmlAttributes);
    }
 
    public static string Stylesheet(this HtmlHelper helper, string[] files)
    {
        return Stylesheet(helper, files, null);
    }
 
    public static string Stylesheet(this HtmlHelper helper, string[] files, object htmlAttributes)
    {
        return Stylesheet(helper, files, new RouteValueDictionary(htmlAttributes));
    }
 
    public static string Stylesheet(this HtmlHelper helper, string[] files, RouteValueDictionary htmlAttributes)
    {
        StringBuilder output = new StringBuilder();
        foreach (var file in files)
        {
            TagBuilder link = new TagBuilder("link");
            link.MergeAttribute("rel", "stylesheet");
            link.MergeAttribute("type", "text/css");
            string absoluteFile = "/Content/css/" + file;
            link.MergeAttribute("href", absoluteFile);
            link.MergeAttributes(htmlAttributes); 
            output.Append(link.ToString(TagRenderMode.SelfClosing));
        }
        return output.ToString();
    }
}