累积列表的值

时间:2019-01-04 22:46:09

标签: c# winforms

我有一个列表,其中每个对象都有两个字段:

  • 日期为DateTime
  • 估计为两倍。

我有一些这样的值:

  • 2019年1月1日2
  • 01/02/2019 3
  • 01/03/2019 4

...等等。

我需要生成另一个相同格式的列表,但要按日期累积“估计”字段。因此结果必须是:

  • 2019年1月1日2
  • 2019年1月2日5(2 + 3)
  • 01/03/2019 9(5 + 4)...等等。

现在,我正在foreach语句中对其进行计算

        for (int iI = 0; iI < SData.TotalDays; iI++)
        {
           DateTime oCurrent = SData.ProjectStart.AddDays(iI);
           oRet.Add(new GraphData(oCurrent, GetProperEstimation(oCurrent)));
        }

然后,我可以对当前日期之前或等于当前日期的所有日期执行Linq Sum:

  private static double GetProperEstimation(DateTime pDate)
  {
     return Data.Where(x => x.Date.Date <= pDate.Date).Sum(x => x.Estimated);
  }

有效。但是问题在于ABSLOUTELLY速度很慢,一个271个元素的列表花费了超过1分钟的时间。

有更好的方法吗?

谢谢。

4 个答案:

答案 0 :(得分:2)

这完全是MoreLinq.Scan

的工作
var newModels = list.Scan((x, y) => new MyModel(y.Date, x.Estimated + y.Estimated));

新模型将具有您想要的值。


(x, y)中,x是上一个项目,y是枚举中的当前项目。


为什么查询速度慢?

因为Where会在每次调用集合时从头开始进行迭代。因此操作数量成倍增长1 + 2 + 3 + ... + n = ((n^2)/2 + n/2)

答案 1 :(得分:2)

您可以编写一个简单的类似LINQ的扩展方法来累加值。该版本被概括为允许不同的输入和输出类型:

static class ExtensionMethods
{
    public static IEnumerable<TOut> Accumulate<TIn, TOut>(this IEnumerable<TIn> source, Func<TIn,double> getFunction, Func<TIn,double,TOut> createFunction)
    {
        double accumulator = 0;

        foreach (var item in source)
        {
            accumulator += getFunction(item);
            yield return createFunction(item, accumulator);
        }
    }
}

用法示例:

public static void Main()
{
    var list = new List<Foo>
    {
        new Foo { Date = new DateTime(2018,1,1), Estimated = 1 },
        new Foo { Date = new DateTime(2018,1,2), Estimated = 2 },
        new Foo { Date = new DateTime(2018,1,3), Estimated = 3 },
        new Foo { Date = new DateTime(2018,1,4), Estimated = 4 },
        new Foo { Date = new DateTime(2018,1,5), Estimated = 5 }
    };
    var accumulatedList = list.Accumulate
    ( 
        (item)      => item.Estimated,                    //Given an item, get the value to be summed
        (item, sum) => new { Item = item, Sum = sum }     //Given an item and the sum, create an output element
    );
    foreach (var item in accumulatedList)
    {
        Console.WriteLine("{0:yyyy-MM-dd} {1}", item.Item.Date, item.Sum);
    }

}

输出:

2018-01-01 1
2018-01-02 3
2018-01-03 6
2018-01-04 10
2018-01-05 15

这种方法只需要对集合进行一次迭代,因此其性能要比一系列求和更好。

Link to DotNetFiddle example

答案 2 :(得分:1)

您可以尝试一下。简单而有效。

var i = 0;

var result = myList.Select(x => new MyObject
{
     Date = x.Date, 
     Estimated = i = i + x.Estimated
}).ToList();

编辑:以这种方式尝试

.Select(x => new GraphData(x.Date, i = i + x.Estimated))

答案 3 :(得分:1)

我会假设您所说的是真实的,您需要的是什么

算法

Create a list or array of values based in the original values ordered date asc
sumValues=0;
foreach (var x in collection){
  sumValues+= x.Estimated; //this will accumulate all the past values and present value
  oRet.Add(x.date, sumValues);
}

第一步(对值进行排序)是最重要的。对于每个将非常快。 参见sort