C#Linq聚合中间值

时间:2017-02-24 16:22:13

标签: c# .net arrays linq

给定一组正数和负数是否有一个可以得到中间值的Linq表达式?

例如

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);
}

此功能将返回[5, 20, 15, 0]

聚合可以以相同的方式使用,它将通过此​​序列

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

我的问题是,有没有办法使用聚合或其他一些方法来返回中间值[5, 20, 15, 0]

6 个答案:

答案 0 :(得分:14)

您需要的是聚合的自定义版本:

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;
  }
}

这是解决您的具体问题的一般机制:

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

现在你可以用

来解决你的问题了
mySequence.MovingSum().Max();

答案 1 :(得分:2)

使用Aggregate

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

结果将是:

[5,20,15,0]

如果您想拥有Max值,可以使用自定义Result类:

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

使用它:

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

您将获得与result.Values和[20]相同的列表result.Max

答案 2 :(得分:1)

您可以在聚合函数中执行此操作:

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

然后使用

intermidiateValues.Max();

答案 3 :(得分:0)

BAD性能的解决方案:

static void Main(string[] args)
{
    var numbers = new[] { 5, 15, -5, -15 };

    var heights =  numbers.Select((o, i) => numbers.Take(i + 1).Sum()).ToList();
    foreach (var item in heights)
    {
        Console.WriteLine(item);
    }
}

它具有O(n ^ 2)复杂度。

答案 4 :(得分:0)

您可以使用自定义投影编写自己的扩展方法来执行累积/运行总计,并将其命名为:

static void Main(string[] args) {
      var numbers = new[] { 5, 15, -5, -15 };

      // results 5, 20, 15, 0
      var results = numbers.Accumulate((a, b) => a + b);
      var max = results.Max(); // 20

      // providing a initial seed value: 20, 35, 30, 15:  
      var result2 = numbers.Accumulate(15, (a, b) => a + b);
      var max2 = result2.Max(); // 35
}

扩展方法:

public static class AccumulateExt
{
    public static IEnumerable<TSource> Accumulate<TSource>(
        this IEnumerable<TSource> source,
        Func<TSource, TSource, TSource> funcAggregate)
    {
        return source.Accumulate(default(TSource), funcAggregate);
    }

    public static IEnumerable<TAggregate> Accumulate<TSource, TAggregate>(
        this IEnumerable<TSource> source,
        TAggregate initialValue,
        Func<TAggregate, TSource, TAggregate> funcAggregate)
    {

        return AccumulateImplementation(source, initialValue, funcAggregate, (_, agg) => agg);
    }

    public static IEnumerable<TResult> Accumulate<TSource, TResult>(
        this IEnumerable<TSource> source,
        Func<TSource, TSource, TSource> funcAggregate,
        Func<TSource, TSource, TResult> resultSelector)
    {

        return source.Accumulate(default(TSource), funcAggregate, resultSelector);

    }

    public static IEnumerable<TResult> Accumulate<TSource, TAggregate, TResult>(
        this IEnumerable<TSource> source,
        TAggregate initialValue,
        Func<TAggregate, TSource, TAggregate> funcAggregate,
        Func<TSource, TAggregate, TResult> resultSelector)
    {
        return AccumulateImplementation(source, initialValue, funcAggregate, resultSelector);
    }


    private static IEnumerable<TResult> AccumulateImplementation<TSource, TAggregate, TResult>(
        this IEnumerable<TSource> source,
        TAggregate initialValue,
        Func<TAggregate, TSource, TAggregate> funcAggregate,
        Func<TSource, TAggregate, TResult> resultSelector)
    {
        var last = initialValue;
        foreach (var item in source)
        {
            var value = funcAggregate(last, item);
            last = value;
            yield return resultSelector(item, value);
        }
    }
}

答案 5 :(得分:-2)

单行版

var numbers = new[] { 5, 15, -5, -15 };
var curHeight = 0;

int best = numbers.Select(x => { curHeight += x; return curHeight; }).Max();

正如埃里克所指出的那样,这是一个不好的做法,即使它在这里起作用也是一种非破坏性的做法

var numbers = new[] { 5, 15, -5, -15 };

int best = Enumerable.Range(1, numbers.Length + 1).Select(x => numbers.Take(x).Sum()).Max();