65.9K
CodeProject is changing. Read more.
Home

Cumulating values with LINQ

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0 vote)

Jul 28, 2012

CPOL

1 min read

viewsIcon

11149

downloadIcon

20

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.