Cumulating values with LINQ
This is an alternative for "Cumulating values with LINQ"
Introduction
This is a 'kind of a' Visual Basic alternative to the original tip.
If you want to achieve the same functionality in VB as in the original C# tip, you would have to create a full iterator class. While it could be done, I started to wonder if it is feasible? Writing an iterator class is somewhat painful and maintaining it can also be a pain.
The problem is that Visual Basic doesn't currently have equivalent for yield return
command. Visual Studio 2012 will change this but since it's not published yet it's not yet a usable solution.
So, instead of creating an iterator class I ended up to a solution where the extension methods are implented using a C# assembly which is simply referenced from the Visual Basic project. Here's a picture of the project setup.
The CumulativeHelper
implemented using C# looks as following:
using System.Linq; namespace CumulativeHelper { /// <summary> /// Class to hold the extension methods /// </summary> public static class CumulativeHelper { /// <summary> /// Builds a cumulative path based on directory names /// </summary> /// <param name="pathPart">Individual directories</param> /// <returns>Cumulated path</returns> public static System.Collections.Generic.IEnumerable<string> CumulativePath( this System.Collections.Generic.IEnumerable<string> pathPart) { System.Text.StringBuilder concatenated = new System.Text.StringBuilder(); foreach (string part in pathPart) { if (concatenated.Length != 0) { concatenated.Append('\\'); } concatenated.Append(part); yield return concatenated.ToString(); } } /// <summary> /// Returns all string items in the collection except the last one /// </summary> /// <param name="stringItem">String items</param> /// <returns>Each string item</returns> public static System.Collections.Generic.IEnumerable<string> AllButLast( this System.Collections.Generic.IEnumerable<string> stringItem) { System.Text.StringBuilder concatenated = new System.Text.StringBuilder(); for (int counter = 0; counter< stringItem.Count() - 1; counter++) { yield return stringItem.ElementAt(counter); } } /// <summary> /// Calculates a cumulative value for decimal numbers /// </summary> /// <param name="numbers">Numbers to sum</param> /// <returns>Cumulative sum</returns> public static System.Collections.Generic.IEnumerable<decimal> CumulativeSum( this System.Collections.Generic.IEnumerable<decimal> numbers) { decimal summedNumber = 0; foreach (decimal number in numbers) { summedNumber = summedNumber + number; yield return summedNumber; } } } }
The functionality is described in the original tip.
Calling from Visual Basic
So the code above handles the LINQ iteration. The Visual Basic project has a project reference to the C# assembly so the VB code can call the functionality. The code for the Visual Basic portion looks like this
Imports CumulativeHelper
Module MainModule
Sub Main()
' Array of test numbers
Dim numbers As Decimal() = New Decimal() {1, 3, 5, 7, 11}
' Calculate and print the cumulative sum for the numbers
System.Diagnostics.Debug.WriteLine("The cumulative sum contains the following results")
For Each partialSum As Decimal In numbers.CumulativeSum()
System.Diagnostics.Debug.WriteLine(" - {0}", partialSum)
Next
System.Diagnostics.Debug.WriteLine("The cumulative sum total is {0}",
numbers.CumulativeSum().Last())
' Some random path
Dim somePath As String = "C:\Some directory\Some subdirectory\Somefile.txt"
' Split the path and print out each cumulated portion of the path
System.Diagnostics.Debug.WriteLine("The path contains the following parts")
For Each partialPath As String In somePath.Split("\").CumulativePath()
System.Diagnostics.Debug.WriteLine(" - '{0}'", New Object() {partialPath})
Next
' Some partially existing path
Dim somePath2 As String = "C:\Windows\Some non-existent directory\Some non-existent file.txt"
' Split the path and print out each cumulated portion of the path
System.Diagnostics.Debug.WriteLine("The path parts are valid as follows")
For Each partialPath As String In somePath2.Split("\").AllButLast().CumulativePath()
System.Diagnostics.Debug.WriteLine(
" - '{0}' does exist: {1}",
partialPath,
System.IO.Directory.Exists(partialPath))
Next
End Sub
End Module
And the result from output is
The cumulative sum contains the following results
- 1
- 4
- 9
- 16
- 27
The cumulative sum total is 27
The path contains the following parts
- 'C:'
- 'C:\Some directory'
- 'C:\Some directory\Some subdirectory'
- 'C:\Some directory\Some subdirectory\Somefile.txt'
The path parts are valid as follows
- 'C:' does exist: True
- 'C:\Windows' does exist: True
- 'C:\Windows\Some non-existent directory' does exist: False
So the same results are achieved. In my opinion extending the LINQ in VB with enumerable methods, it may be much easier and clearer to use C# for the extension methods. The implementation is much smaller and more easily maintainable and I think that the C# code is quite easily understandable even with almost no experience with C# programming.
History
-
July 28, 2012: Alternative created.