Как суммировать периоды между датами в linq c # до определенного периода - PullRequest
0 голосов
/ 06 мая 2019

У меня есть таблица данных, которая содержит три столбца, мне нужно проверить, когда каждый ID сотрудника достиг двух или достигнет двух лет, вычитая Date1 из Date2 и суммируя разницу по запросу LINQ.

Если Date2 значение равно нулю, это означает, что ID все еще работает до сих пор.

   ID                    Date1                                 Date2
>  100              10/01/2016                               09/01/2017               
>  100              20/09/2017                               25/05/2019  
>  101              05/07/2018

Мне нужен вывод, как показано ниже:

   ID                two_years
> 100                    19/09/2018   
> 101                    04/07/2020

1 Ответ

0 голосов
/ 07 мая 2019

Используя мое расширение Scan, основанное на операторе сканирования APL (например, Aggregate, только оно возвращает промежуточные результаты):

public static class IEnumerableExt {
    // TRes seedFn(T FirstValue)
    // TRes combineFn(TRes PrevResult, T CurValue)
    public static IEnumerable<TRes> Scan<T, TRes>(this IEnumerable<T> src, Func<T, TRes> seedFn, Func<TRes, T, TRes> combineFn) {
        using (var srce = src.GetEnumerator()) {
            if (srce.MoveNext()) {
                var prev = seedFn(srce.Current);

                while (srce.MoveNext()) {
                    yield return prev;
                    prev = combineFn(prev, srce.Current);
                }
                yield return prev;
            }
        }
    }
}

Тогда, предположив, что через два года вы имеете в виду 2 *365 дней, и при условии, что вы посчитаете даты начала и окончания каждого периода как часть общей суммы, этот LINQ найдет ответ:

var ans = src.Select(s => new { s.ID, s.Date1, s.Date2, Diff = (s.Date2.HasValue ? s.Date2.Value-s.Date1 : DateTime.Now.Date-s.Date1).TotalDays+1 })
             .GroupBy(s => s.ID)
             .Select(sg => new { ID = sg.Key, sg = sg.Scan(s => new { s.Date1, s.Date2, s.Diff, DiffAccum = s.Diff }, (res, s) => new { s.Date1, s.Date2, s.Diff, DiffAccum = res.DiffAccum + s.Diff }) })
             .Select(IDsg => new { IDsg.ID, two_year_base = IDsg.sg.FirstOrDefault(s => s.DiffAccum > twoYears) ?? IDsg.sg.Last() })
             .Select(s => new { s.ID, two_years = s.two_year_base.Date1.AddDays(twoYears-(s.two_year_base.DiffAccum - s.two_year_base.Diff)).Date });

Если ваши исходные данные не отсортированы в порядке Date1,или ID + Date1 порядок, вам нужно будет добавить OrderBy для сортировки по Date1.

Объяснение: Сначала мы вычисляем рабочие дни (?), представленные каждой строкой, используя сегодня, еслиу нас нет окончания Date2.Затем группируйте по идентификатору и работайте над каждой группой.Для каждого идентификатора мы вычисляем текущую сумму отработанных дней.Затем найдите Date1, который следует за двухлетней отметкой, используя текущую сумму (DiffAccum) и вычислите двухлетнюю дату из Date1 и оставшееся время, необходимое в этом периоде.

Если это возможноконкретный идентификатор может иметь много периодов, вы можете использовать другой вариант Scan, ScanUntil, который оценивает короткие замыкания на основе предиката.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...