Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Linq aggregate intermediate values

Tags:

arrays

c#

.net

linq

Given an array of positive and negative numbers is there a Linq expression that can get intermediate values?

for example

var heights = new List<int>();    
var numbers = new [] { 5, 15, -5, -15 };    
var curHeight = 0;

foreach (var number in numbers)
{
    curHeight = curHeight + number;
    heights.add(curHeight);
}

This function will return [5, 20, 15, 0]

Aggregate can be used the same way and it would go through this sequence

numbers.aggregate((a, b) => a + b);
0 + 5 = 5, 5 + 15 = 20, 20 - 5 = 15, 15 - 15 = 0

My question is, is there a way to use aggregate or some other so that the intermediate values [5, 20, 15, 0] are returned?

like image 525
Adam Avatar asked Feb 24 '17 16:02

Adam


3 Answers

What you need is a custom version of aggregate:

public static IEnumerable<R> AggregateSequence<A, R>(
  this IEnumerable<A> items,
  Func<A, R, R> aggregator,
  R initial)
{
  // Error cases go here.
  R result = initial;
  foreach(A item in items)
  {
    result = aggregator(item, result);
    yield return result;
  }
}

That is a general mechanism for solving your specific problem:

public static IEnumerable<int> MovingSum(this IEnumerable<int> items)
{
  return items.AggregateSequence( (item, sum) => item + sum, 0 );
}

And now you can solve your problem with

mySequence.MovingSum().Max();
like image 65
Eric Lippert Avatar answered Oct 17 '22 11:10

Eric Lippert


With usage of Aggregate:

var result = numbers.Aggregate(new List<int>(), (a,b)=>
{
    a.Add(a.LastOrDefault()+b);
    return a;
});

result wil be:

[5,20,15,0]

If you want to have a Max value, you could use custom Result class:

public class Result
{
    public List<int> Values {get;set;}
    public int Max => Values.Max();
    public Result()
    {
        Values = new List<int>();
    }
}

use it:

var result = numbers.Aggregate(new Result(), (a,b)=>
{
    a.Values.Add(a.Values.LastOrDefault()+b);       
    return a;
});

and you will get the same List as result.Values and [20] as result.Max

like image 3
Maksim Simkin Avatar answered Oct 17 '22 11:10

Maksim Simkin


You can do this stuff within your aggregate function:

var intermidiateValues = new List<int>();
numbers.aggregate((intermidiateValue, next) => {
    intermidiateValues.Add(intermidiateValue);
    return intermidiateValue + next;
});

And then use

intermidiateValues.Max();
like image 1
Bagdan Gilevich Avatar answered Oct 17 '22 12:10

Bagdan Gilevich