2015-04-22 4 views
3

У меня есть результат запроса linq, как показано на рисунке. В последнем запросе (не показан) я группирую Year by LeaveType. Тем не менее, я хочу рассчитать текущую итоговую сумму для leaveCarriedOver для каждого типа в течение нескольких лет. То есть, больной LeaveCarriedOver в 2010 году становится «открытие» баланс по болезни в 2011 году, плюс один на 2011Как вычислить общее количество с использованием linq

enter image description here

Я сделал еще один запрос на показанном списке результатов, который выглядит как:

  var leaveDetails1 = (from l in leaveDetails 
      select new 
      { 
       l.Year, 
       l.LeaveType, 
       l.LeaveTaken, 
       l.LeaveAllocation, 
       l.LeaveCarriedOver, 
       RunningTotal = leaveDetails.Where(x => x.LeaveType == l.LeaveType).Sum(x => x.LeaveCarriedOver) 
      }); 

где leaveDetails - результат изображения.

В результате RunningTotal не является кумулятивным, как ожидалось. Как я могу достичь своей первоначальной цели. Открыта для любых идей - моим последним вариантом будет сделать это в javascript в интерфейсе. Заранее спасибо

+1

Возможно, вы можете использовать метод Aggregate из LINQ: https://msdn.microsoft.com/en-us/library/system.linq.enumerable.aggregate.aspx. Это эквивалентно сгибанию функционального программирования. –

+0

Я расскажу вам, как это решить. Вы можете сделать это, получив «отдельный» <»« Список > 'of Year (' int') и LeaveType (я предполагаю, что это 'string'). Чтобы получить это, вам следует предоставить простой «Distinct <>», за которым следует «Выбрать <>». Затем этот список - это сумма, которую вы можете фактически накапливать. Теперь, используя этот новый список, вам нужно сделать 'Select <>' и вернуть новый объект, поэтому давайте скажем 'Tuple ', где первый параметр 2 совпадает с другим списком, но последний int you нужен список деталей с 'Where <> 'для фильтрации для year/leavetype, а затем возвращаем' Sum <> 'этого. – Franck

+0

@Franck ваш комментарий имеет смысл для меня, но я просто не получаю права с моим запросом linq. Можете ли вы отправить ответ на эту тему. благодаря – mboko

ответ

0

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

получение отдельного списка Year и LeaveType - это группа по и выберите первую из каждой группы. мы возвращаем List<Tuple<int, string>> где Int это год и string является LeaveType

var distinctList = leaveDetails1.GroupBy(data => new Tuple<int, string>(data.Year, data.LeaveType)).Select(data => data.FirstOrDefault()).ToList(); 

мы хотим общий для каждого из этих элементов, так что вы хотите выбрать из этого списка, чтобы вернуть идентификатор (Year и LeaveType) плюс общее поэтому дополнительное значение для Tuple<int, string, int>.

var totals = distinctList.Select(data => new Tuple<int, string, int>(data.Year, data.LeaveType, leaveDetails1.Where(detail => detail.Year == data.Year && detail.LeaveType == data.LeaveType).Sum(detail => detail.LeaveCarriedOver))).ToList(); 

читает строку выше вы можете видеть, что принимать различные суммы, которые мы хотим перечислить, создать новый объект, хранить Year и LeaveType для справки установите последнюю Int с Sum<> отфильтрованных деталей для этого Year и LeaveType.

0

Если я полностью понимаю, что вы пытаетесь сделать, я не думаю, что я буду полагаться исключительно на встроенные операторы LINQ. Я думаю (акцент на мысли), что любая комбинация встроенных операторов LINQ собирается решить эту проблему в O (n^2) времени исполнения.

Если бы я собирался осуществить это в LINQ, то я хотел бы создать метод расширения для IEnumerable, который похож на Scan функции в реактивных расширений (или найти библиотеку, что там уже реализовали):

public static class EnumerableExtensions 
{ 
    public static IEnumerable<TAccumulate> Scan<TSource, TAccumulate>(
     this IEnumerable<TSource> source, 
     TAccumulate seed, 
     Func<TAccumulate, TSource, TAccumulate> accumulator) 
    { 
     // Validation omitted for clarity. 
     foreach(TSource value in source) 
     { 
      seed = accumulator.Invoke(seed, value); 
      yield return seed; 
     } 
    } 
} 

Тогда это должно сделать это вокруг O (п § п) (из-за заказа по операциям):

leaveDetails 
.OrderBy(x => x.LeaveType) 
.ThenBy(x => x.Year) 
.Scan(new { 
     Year = 0, 
     LeaveType = "Seed", 
     LeaveTaken = 0, 
     LeaveAllocation = 0.0, 
     LeaveCarriedOver = 0.0, 
     RunningTotal = 0.0 
    }, 
    (acc, x) => new { 
     x.Year, 
     x.LeaveType, 
     x.LeaveTaken, 
     x.LeaveAllocation, 
     x.LeaveCarriedOver, 
     RunningTotal = x.LeaveCarriedOver + (acc.LeaveType != x.LeaveType ? 0 : acc.RunningTotal) 
    }); 

Вы не говорите, но я предполагаю, что данные поступают из базы данных; если это так, то вы можете получить leaveDetails назад уже отсортировано и пропустить сортировку здесь. Это приведет вас к O (n).

Если вы не хотите создавать метод расширения (или найдите его), то это приведет к тому же (просто уродливым способом).

var temp = new 
    { 
     Year = 0, 
     LeaveType = "Who Cares", 
     LeaveTaken = 3, 
     LeaveAllocation = 0.0, 
     LeaveCarriedOver = 0.0, 
     RunningTotal = 0.0 
    }; 
var runningTotals = (new[] { temp }).ToList(); 
runningTotals.RemoveAt(0); 

foreach(var l in leaveDetails.OrderBy(x => x.LeaveType).ThenBy(x => x.Year)) 
{ 
    var s = runningTotals.LastOrDefault(); 
    runningTotals.Add(new 
     { 
      l.Year, 
      l.LeaveType, 
      l.LeaveTaken, 
      l.LeaveAllocation, 
      l.LeaveCarriedOver, 
      RunningTotal = l.LeaveCarriedOver + (s == null || s.LeaveType != l.LeaveType ? 0 : s.RunningTotal) 
     }); 
} 

Это также должно быть O (N журнал N) или O (п), если вы можете предварительно сортировать leaveDetails.

0

Если я правильно понял вопрос, который вы хотите что-то вроде

decimal RunningTotal = 0; 

var results = leaveDetails 
    .GroupBy(r=>r.LeaveType) 
    .Select(r=> new 
    { 
     Dummy = RunningTotal = 0 , 
     results = r.OrderBy(o=>o.Year) 
     .Select(l => new 
      { 
       l.Year, 
       l.LeaveType , 
       l.LeaveAllocation, 
       l.LeaveCarriedOver, 
       RunningTotal = (RunningTotal = RunningTotal + l.LeaveCarriedOver) 
      }) 
    }) 
    .SelectMany(a=>a.results).ToList(); 

Это в основном с помощью перегрузки Select<TSource, TResult> рассчитать текущий остаток, но первый сгруппированных по LeaveType поэтому мы можем сбросить RunningTotal для каждого LeaveType, а затем разгруппированы в конце.

Смежные вопросы