Click here to Skip to main content
15,881,715 members
Articles / Programming Languages / C#
Alternative
Tip/Trick

Cumulating Values with LINQ

Rate me:
Please Sign up or sign in to vote.
4.33/5 (2 votes)
27 Jul 2012CPOL 12.6K   35   2   2
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:

C#
/// <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):

C#
/// <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):

C#
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:

C#
  // 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Retired
United States United States
I started programming in Basic on a DECSystem-10 as a Freshman at Caltech in 1974. I quickly transitioned to assembly language, Fortran, and Pascal. As a summer job at JPL, I did analysis of fuel consumption for the Viking Mars Orbiter attitude control system. I also spent a summer doing O/S maintenance at Digital Equipment Corporation.
After graduation, I started developing microprocessor development tools (e.g., cross-compiler, debugger) for Beckman Instruments, a scientific instrument company.
I've worked on custom file-systems, a real-time O/S for Z8000, Expert Systems (SpinPro™ & PepPro™), and internal and external networking support (I was their first webmaster).
I've worked on the DNA analysis system.
I was the console/UI software architect for Ultracentrifuges and protein Capillary Electrophoresis (CE) systems.
After 35 years, Danaher having acquired Beckman (now Beckman Coulter), transferred the CE group to become part of Sciex (2014), and was on the software team that developed the new (9/2021) Sciex BioPhase Capillary Electrophoresis instrument.
---
Finally, after 43 years, 7 months, and 19 days, I am retired.

Comments and Discussions

 
QuestionInclude link to referenced Tip please Pin
tonyt30-Jul-12 13:31
tonyt30-Jul-12 13:31 
EOM
AnswerRe: Include link to referenced Tip please Pin
Matt T Heffron30-Jul-12 14:47
professionalMatt T Heffron30-Jul-12 14:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.