Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Cumulating values with LINQ

0.00/5 (No votes)
27 Jul 2012 1  
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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here