I have the following function which I am using to find the terminal accumulative positive and negative value, which is working:
public class CumulativeTotal
{
[Test]
public void CalculatesTerminalValue()
{
IEnumerable<decimal> sequence = new decimal[] { 10, 20, 20, -20, -50, 10 };
var values = FindTerminalValues(sequence);
Assert.That(values.Item1, Is.EqualTo(-20));
Assert.That(values.Item2, Is.EqualTo(50));
Assert.Pass();
}
public static Tuple<decimal,decimal> FindTerminalValues(IEnumerable<decimal> values)
{
decimal largest = 0;
decimal smallest = 0;
decimal current = 0;
foreach (var value in values)
{
current = value;
if (current > largest)
largest = current;
else if (current < smallest)
smallest = current;
}
return new Tuple<decimal, decimal>(smallest,largest);
}
}
However, in the interests of learning, how could i implement with Linq?
I can see a package MoreLinq, but not sure where to start!
CodePudding user response:
yes, you can use MoreLinq like this, it has the Scan method.
public static Tuple<decimal, decimal> FindTerminalValues(IEnumerable<decimal> values)
{
var cumulativeSum = values.Scan((acc, x) => acc x).ToList();
decimal min = cumulativeSum.Min();
decimal max = cumulativeSum.Max();
return new Tuple<decimal, decimal>(min, max);
}
The Scan extension method generates a new sequence by applying a function to each element in the input sequence, using the previous element as an accumulator. In this case, the function is simply the addition operator, so the Scan method generates a sequence of the cumulative sum of the input sequence.
CodePudding user response:
You can try standard Linq Aggregate
method:
// Let's return named tuple: unlike min, max
// current .Item1 and .Item2 are not readable
public static (decimal min, decimal max) FindTerminalValues(IEnumerable<decimal> values) {
//public method arguments validation
if (values is null)
throw new ArgeumentNullValue(nameof(values));
(var min, var max, _) = values
.Aggregate((min : 0m, max : 0m, curr : 0m), (s, a) => (
Math.Min(s.min, s.curr a),
Math.Max(s.max, s.curr a),
s.curr a));
return (min, max);
}
CodePudding user response:
You can use the Aggregate method in LINQ to achieve this. The Aggregate method applies a function to each element in a sequence and returns the accumulated result. It takes as parameter an initial accumulator object to keep track of the smallest and largest function.
public static Tuple<decimal,decimal> FindTerminalValues(IEnumerable<decimal> values)
{
return values.Aggregate(
// Initial accumulator value:
new Tuple<decimal, decimal>(0, 0),
// Accumulation function:
(acc, value) =>
{
// Add the current value to the accumulator:
var current = acc.Item1 value;
// Update the smallest and largest accumulated values:
var smallest = Math.Min(current, acc.Item1);
var largest = Math.Max(current, acc.Item2);
// Return the updated accumulator value:
return new Tuple<decimal, decimal>(smallest, largest);
});
}