Partilhar via


Aggregation - VB

[Table of Contents] [Next Topic]

There are many times when composing queries that you have to do aggregation. There are a number of built-in aggregators:

This blog is inactive.
New blog: EricWhite.com/blog

Blog TOC·         Count

·         LongCount

·         Sum

·         Min

·         Max

·         Average

·         Aggregate

Aggregators are extension methods that operate on IEnumerable(Of T), and return some type T (or U) that is not a collection.  Another way to say it: aggregation is the process of taking a collection and making a singleton.  Using the Sum aggregate extension method can be as simple as:

Dim ia() As Integer = {1, 5, 2, 6, 7}
Console.WriteLine(ia.Sum())

The Aggregate operator is a general purpose aggregator.  You can use it to sum an array, concatenate a bunch of strings, or anything else where you need to take a collection and reduce it to a single type.  It has a couple of different forms.  If you want to use it to aggregate a collection of some type T into a single T, you can use it like this:

Dim ia() As Integer = {1, 5, 2, 6, 7}
Console.WriteLine(ia.Aggregate(Function(a, i) a + i))

This does the same thing as Sum.

There is another overload of the Aggregate extension method that allows you to specify a seed for the aggregation - in other words, to provide an initial value for aggregation.  The following code shows setting the seed to zero:

Dim ia() as Integer = {1, 5, 2, 6, 7}
Console.WriteLine(ia.Aggregate(0, Function(a, i) a + i))

This will produce the same results as the previous example. 

To use the Aggregate operator to concatenate strings, you could do this:

Dim ia() As String = {"aaa", "bbb", "ccc"}
Console.WriteLine(ia.Aggregate(Function(a, i) a & i))

The observant will notice that this use of Aggregate creates a new short-lived string object on the heap for every item in the collection.

You can use Aggregate in combination with the StringBuilder class, as follows:

Dim ia() As String = {"aaa", "bbb", "ccc"}
Console.WriteLine(ia.Aggregate(New StringBuilder, _
Function(sb, i) sb.Append(i), _
Function(sb) sb.ToString))

This example uses an overload of the Aggregate extension method that allows you to set the seed (where it news up the StringBuilder), and to specify another lambda expression that projects the results of the aggregation.  In this case, the projection is to convert the StringBuilder to a string.

This eliminates the creation of so many strings on the heap, but it adds syntactic complexity.  It also isn't pure, as the side effect of the changing StringBuilder would be observable in the lambda expression, but I don't care.  This is a side effect that I can live with.

But the best method to concatenate strings is to write an extension method, StringConcatenate, as follows:

<System.Runtime.CompilerServices.Extension()> _
Function StringConcatenate(source As IEnumerable(Of String)) _
as String
Return source.Aggregate(New StringBuilder, _
Function(sb, i) sb.Append(i), _
Function(sb) sb.ToString)
End Function

Its use:

Dim ia() As String = {"aaa", "bbb", "ccc"}
Console.WriteLine(ia.StringConcatenate)

Finally, it is useful to have another overload of StringConcatenate.

This one that takes a delegate that does the projection from T to string:

<System.Runtime.CompilerServices.Extension()> _
Function StringConcatenate(Of T) _
(source As IEnumerable(Of T), projectionFunc As Func(Of T, String)) _
as String
Return source.Aggregate(New StringBuilder, _
Function(sb, i) sb.Append(projectionFunc(i)), _
Function(sb) sb.ToString)
End Function

Here is a small program that contains both extension methods, and code to exercise them:

Imports System.Text

Module Module1
<System.Runtime.CompilerServices.Extension()> _
Function StringConcatenate(source As IEnumerable(Of String)) _
as String
Return source.Aggregate(New StringBuilder, _
Function(sb, i) sb.Append(i), _
Function(sb) sb.ToString)
End Function

<System.Runtime.CompilerServices.Extension()> _
Function StringConcatenate(Of T) _
(source As IEnumerable(Of T), projectionFunc As Func(Of T, String)) _
as String
Return source.Aggregate(New StringBuilder, _
Function(sb, i) sb.Append(projectionFunc(i)), _
Function(sb) sb.ToString)
End Function

Sub Main()
Dim stringList() As String = {"aaa", "bbb", "ccc"}
Dim xmlDoc = _
<Root>
<Value>111</Value>
<Value>222</Value>
<Value>333</Value>
</Root>
Dim s1 = stringList.StringConcatenate()
Dim s2 = xmlDoc.Elements().StringConcatenate(Function(el) CStr(el))
Console.WriteLine(s1)
Console.WriteLine(s2)
End Sub
End Module

[Table of Contents] [Next Topic] [Blog Map]

Examples.txt

Comments

  • Anonymous
    November 24, 2008
    Why not just use the Aggregate query expression in VB?