65.9K
CodeProject is changing. Read more.
Home

Cumulating Values with LINQ

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (2 votes)

Jul 27, 2012

CPOL
viewsIcon

12922

downloadIcon

35

This is an alternative for "Cumulating values with LINQ"

Introduction

This is a fully generic implementation of the tip described by Mika Wendelius.

Using the Code

The simple extension method which handles a simple accumulation (like the CumulativeSum of the original tip) is this:

/// <summary>
/// Transforms a sequence into a new sequence by accumulating the values by a specified method.
/// </summary>
/// <typeparam name="Titems">
/// The type of the items in the initial and result sequences, and the accumulated value.
/// </typeparam>
/// <param name="sequence">The input sequence.
/// </param>
/// <param name="accumulateOperation">The operation which accumulates the values. 
/// The parameters are (previousAccumulatedValue, itemFromSequence).
/// </param>
/// <returns>A new sequence of the accumulated values.
/// </returns>
public static IEnumerable<Titems> CumulativeSequence<Titems>
	(this IEnumerable<Titems> sequence,
    Func<Titems, Titems, Titems> accumulateOperation)
{
  return CumulativeSequence(sequence, default(Titems), accumulateOperation, a => a);
}

This clearly just defers to a fully parameterized implementation (which is necessary to implement the CumulativePath functionality of the original tip):

/// <summary>
/// Transforms a sequence into a new sequence by accumulating the values by a specified method.
/// </summary>
/// <typeparam name="Titems">
/// The type of the items in the initial sequence.
/// </typeparam>
/// <typeparam name="Taccum">
/// The type of the accumulator object.
/// </typeparam>
/// <typeparam name="Tvalues">
/// The type of the values in the result sequence.
/// </typeparam>
/// <param name="sequence">The input sequence.
/// </param>
/// <param name="accumulatorInitializer">
/// The initializer for the accumulator object.
/// </param>
/// <param name="accumulateOperation">The operation which accumulates the values. 
/// The parameters are (accumulatorObject, itemFromSequence).
/// It returns the updated or new accumulatorObject.
/// </param>
/// <param name="valueOperation">
/// The operation which gets the result sequence value from the accumulator object.
/// </param>
/// <returns>A new sequence of the accumulated values.
/// </returns>
public static IEnumerable<Tvalues> CumulativeSequence<Titems, Taccum, 
			Tvalues>(this IEnumerable<Titems> sequence,
			Taccum accumulatorInitializer,
			Func<Taccum, Titems, Taccum> accumulateOperation,
			Func<Taccum, Tvalues> valueOperation)
{
  Taccum accumulator = accumulatorInitializer;
  return sequence.Select(item => {
    accumulator = accumulateOperation(accumulator, item);
    return valueOperation(accumulator);
  });
}

(using statements are omitted, see the attached code file.)

Here is the usage corresponding to the CumulativeSum (these give the same output as the original tip):

private static decimal Sum(decimal a, decimal b) { return a + b; }
static void Main()
{
  decimal[] numbers = new decimal[] { 1, 3, 5, 7, 11 };
  foreach (decimal partialSum in numbers.CumulativeSequence((a, n) => a + n))
  {
    Debug.Print(" - {0}", partialSum);
  }
  // can use a defined method instead of repeating the same lambda
  Debug.Print("The cumulative sum total is {0}", numbers.CumulativeSequence(Sum).Last());
  // ...

The CumulativePath of the original tip used a StringBuilder for the path accumulation, and is implemented like this:

  // Some random path
  var splitPath = @"C:\Some directory\Some subdirectory\Somefile.txt".Split('\\');

  // Split the path and print out each cumulated portion of the path
  Debug.Print("The path contains the following parts");
  var pathSequence = splitPath.CumulativeSequence(new StringBuilder(),
                                                  (a, p) => {
                                                    if (a.Length != 0)
                                                      a.Append('\\');
                                                    a.Append(p);
                                                    return a;
                                                  },
                                                  a => a.ToString());
  foreach (string partialPath in pathSequence)
  {
    Debug.Print("   - '{0}'", partialPath);
  }
}

History

  • July 27, 2012 - Initial alternative