Накапливать значения списка - PullRequest
0 голосов
/ 05 января 2019

У меня есть список, в котором каждый объект имеет два поля:

  • Date as DateTime
  • Оценивается как двойной.

У меня есть несколько таких значений:

  • 01/01/2019 2
  • 01/02/2019 3
  • 01/03/2019 4

... и так.

Мне нужно сгенерировать другой список, того же формата, но с накоплением оценочного поля, дата за датой. Таким образом, результат должен быть:

  • 01/01/2019 2
  • 01/02/2019 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 для всех дат, предшествующих или равных текущей дате:

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

Это работает. Но проблема в том, что он АБСОЛЮТНО медленен и занимает более 1 минуты для списка из 271 элемента.

Есть ли лучший способ сделать это?

Заранее спасибо.

Ответы [ 4 ]

0 голосов
/ 05 января 2019

Вы можете написать простой 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

Этот подход потребует только одной итерации по набору, поэтому должен работать намного лучше, чем ряд сумм.

Ссылка на пример DotNetFiddle

0 голосов
/ 05 января 2019

Я буду считать, что то, что вы сказали, реально, что вам нужно, хе-хе

Алгоритм

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

Первый шаг (упорядочение значений) является наиболее важным. Для каждого будет очень быстро. см сортировка

0 голосов
/ 05 января 2019

Это точно работа MoreLinq.Scan

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

Новые модели будут иметь нужные значения.


in (x, y), x - предыдущий элемент, а y - текущий элемент в перечислении.


Почему ваш запрос медленный?

потому что Where будет повторять вашу коллекцию с самого начала каждый раз, когда вы ее вызываете. так что количество операций растет в геометрической прогрессии 1 + 2 + 3 + ... + n = ((n^2)/2 + n/2).

0 голосов
/ 05 января 2019

Вы можете попробовать это. Простой, но эффективный.

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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...